source: Evidence/Bridge.cc@ 245

Last change on this file since 245 was 232, checked in by ogrimm, 14 years ago
Made Lock()/Unlock() public, automatic configuration tracking for Bridge
File size: 7.2 KB
Line 
1/********************************************************************\
2
3 Bridge between two networks
4
5 Subscription to top-level server list DIS_DNS/SERVER_LIST not via
6 AddService() ensures AddService() only called from infoHandler(),
7 thus serialized by DIM and no Mutex is necessary.
8
9 Remote procedure calls are bridged through their underlying services
10 and commands.
11
12 Configuraton changes are automatically tracked through ConfigChanged()
13
14 Oliver Grimm, June 2010
15
16\********************************************************************/
17
18#define SERVER_NAME "Bridge"
19#include "Evidence.h"
20
21#include <string>
22#include <vector>
23#include <regex.h>
24
25const int DEFAULT_PORT = 2505;
26
27using namespace std;
28
29// Class declaration
30class Bridge: public DimClient, public EvidenceServer {
31
32 struct Item {
33 DimCommand *Command;
34 DimStampedInfo *DataItem;
35 DimService *Service;
36 char *Data;
37 };
38
39 map<string, struct Item> Map;
40 vector<regex_t> RegEx;
41 DimInfo *ServerList;
42
43 void infoHandler();
44 void commandHandler();
45 void AddService(string, char *, int);
46 void RemoveService(string);
47 void ConfigChanged();
48 void BugFix();
49 void Undo();
50
51 public:
52 Bridge(char *, int);
53 ~Bridge();
54};
55
56// Constructor
57Bridge::Bridge(char *Name, int Port): EvidenceServer(SERVER_NAME) {
58
59 // Set primary DIM network to subscribe from
60 DimClient::setDnsNode(Name, Port);
61
62 // Initialise and request notification of configuration changes
63 ServerList = NULL;
64 GetConfig("cmdallow", " ");
65 ActivateSignal(SIGUSR2);
66}
67
68
69// Destructor
70Bridge::~Bridge() {
71
72 Undo();
73}
74
75
76// Undo: Delete all subscriptions and regular expressions
77void Bridge::Undo() {
78
79 while (Map.size() != 0) RemoveService((*Map.begin()).first);
80 delete ServerList;
81 for (int i=0; i<RegEx.size(); i++) regfree(&RegEx[i]);
82 RegEx.clear();
83}
84
85
86// Called by signal handler in case configuration changes (also initially)
87void Bridge::ConfigChanged() {
88
89 static string ExcludeString;
90
91 // Check if configuratiomn changed
92 if (ExcludeString == GetConfig("exclude")) return;
93 ExcludeString = GetConfig("exclude");
94
95 // Remove all previous subscriptions
96 Undo();
97
98 // Compile regular expressions
99 regex_t R;
100 vector<string> Exclude = Tokenize(ExcludeString, " \t");
101 for (int i=0; i<Exclude.size(); i++) {
102 int Ret = regcomp(&R, Exclude[i].c_str(), REG_EXTENDED|REG_NOSUB);
103 if (Ret != 0) {
104 char Err[200];
105 regerror(Ret, &R, Err, sizeof(Err));
106 Message(ERROR, "Error compiling regular expression '%s' (%s)", Exclude[i].c_str(), Err);
107 }
108 else RegEx.push_back(R);
109 }
110
111 // Subscribe to top-level server list
112 ServerList = new DimInfo((char *) "DIS_DNS/SERVER_LIST", NO_LINK, this);
113}
114
115
116// Service subscription and repeating
117void Bridge::infoHandler() {
118
119 DimInfo *I = getInfo();
120
121 // Check if service available
122 if (!ServiceOK(I)) return;
123
124 // If service is DIS_DNS/SERVER_LIST, subscribe to all SERVICE_LIST services
125 if (strcmp(I->getName(), "DIS_DNS/SERVER_LIST") == 0) {
126
127 char *Token = strtok(I->getString(), "+-!@");
128 while (Token != NULL) {
129 if (*I->getString() == '-' || *I->getString() == '!') {
130 RemoveService(string(Token)+"/SERVICE_LIST");
131 }
132 else AddService(string(Token)+"/SERVICE_LIST", (char *) "C", DimSERVICE);
133
134 // Skip server IP address and process ID
135 Token = strtok(NULL, "|");
136 Token = strtok(NULL, "@");
137 }
138 return;
139 }
140
141 // If service is SERVICE_LIST, scan and subscribe/unsubscribe to services
142 if (strstr(I->getName(), "/SERVICE_LIST") != NULL) {
143
144 // Bug fix for empty SERVICE_LIST
145 if (strlen(I->getString()) == 0) {
146 string Tmp(I->getName());
147 RemoveService(I->getName());
148 AddService(Tmp.c_str(), (char *) "C", DimSERVICE);
149 return;
150 }
151
152 char *Format, *Name = strtok(I->getString(), "+-!|");
153 while (Name != NULL) {
154 if ((Format = strtok(NULL, "\n")) != NULL) {
155 // Check if service added or removed/unavailable
156 if (*I->getString() == '-' || *I->getString() == '!') {
157 if (strstr(Format, "|RPC") != NULL) {
158 RemoveService(string(Name)+"/RpcIn");
159 RemoveService(string(Name)+"/RpcOut");
160 }
161 else RemoveService(Name);
162 }
163 else {
164 // Determine type of service
165 if (strstr(Format, "|CMD") != NULL) {
166 *(strstr(Format, "|CMD")) = '\0';
167 AddService(Name, Format, DimCOMMAND);
168 }
169 else if (strstr(Format, "|RPC") != NULL) {
170 *(strstr(Format, "|RPC")) = '\0';
171 if (strchr(Format, ',') != NULL) {
172 *strchr(Format, ',') = '\0';
173 AddService(string(Name)+"/RpcIn", Format, DimCOMMAND);
174 AddService(string(Name)+"/RpcOut", Format+strlen(Format)+1, DimSERVICE);
175 }
176
177 }
178 else {
179 Format[strlen(Format)-1] = '\0';
180 AddService(Name, Format, DimSERVICE);
181 }
182 }
183 }
184 Name = strtok(NULL, "|");
185 }
186 return;
187 }
188
189 // Check if service known and repeat to secondary DNS
190 if (Map.count(I->getName()) == 0) return;
191
192 // Copy service data
193 delete[] Map[I->getName()].Data;
194 Map[I->getName()].Data = new char [I->getSize()];
195 memcpy(Map[I->getName()].Data, I->getData(), I->getSize());
196
197 // Set new service properties and update service
198 Map[I->getName()].Service->setQuality(I->getQuality());
199 Map[I->getName()].Service->setTimestamp(I->getTimestamp(), I->getTimestampMillisecs());
200 Map[I->getName()].Service->updateService(Map[I->getName()].Data, I->getSize());
201}
202
203
204// Command repeating (also handles requests for remote procedure calls)
205void Bridge::commandHandler() {
206
207 // Check if client allowed to send commands
208 vector<string> Client = Tokenize(getClientName(), "@");
209 if (Client.size() == 2 && GetConfig("cmdallow").find(Client[1]) == string::npos) {
210 Message(INFO, "Rejected command/rpc from %s (ID %d)", getClientName(), getClientId());
211 return;
212 }
213
214 // Send command to server
215 sendCommandNB(getCommand()->getName(), getCommand()->getData(), getCommand()->getSize());
216}
217
218
219// Service subscription
220void Bridge::AddService(string Name, char *Format, int Type) {
221
222 // Check if already subscribed to this service
223 if (Map.count(Name) != 0) return;
224
225 // Should service be ignored?
226 for (int i=0; i<RegEx.size(); i++) {
227 if (regexec(&RegEx[i], Name.c_str(), (size_t) 0, NULL, 0) == 0) return;
228 }
229
230 // Create subscription and service to secondary DNS or new command
231 Map[Name].Command = NULL;
232 Map[Name].DataItem = NULL;
233 Map[Name].Service = NULL;
234 Map[Name].Data = NULL;
235
236 if (Type == DimSERVICE) {
237 Map[Name].Service = new DimService(Name.c_str(), (char *) Format, Map[Name].Data, 0);
238 Map[Name].DataItem = new DimStampedInfo(Name.c_str(), NO_LINK, this);
239 }
240 else if (Type == DimCOMMAND) Map[Name].Command = new DimCommand(Name.c_str(), Format, this);
241}
242
243
244// Remove service from watch list (unused pointer are NULL)
245void Bridge::RemoveService(string Name) {
246
247 // Check if actually subscribed to service
248 if (Map.count(Name) == 0) return;
249
250 delete Map[Name].DataItem;
251 delete Map[Name].Service;
252 delete[] Map[Name].Data;
253 delete Map[Name].Command;
254
255 Map.erase(Name);
256}
257
258
259// Main program
260int main(int argc, char *argv[]) {
261
262 // Check command line argument
263 if (argc == 1) {
264 printf("Usage: %s <address of primary DNS> [port] (default port %d)\n", argv[0], DEFAULT_PORT);
265 exit(EXIT_FAILURE);
266 }
267
268 // Static ensures calling of destructor by exit()
269 static Bridge Class(argv[1], argc>2 ? atoi(argv[2]) : DEFAULT_PORT);
270
271 // Sleep until signal caught
272 while (!Class.ExitRequest) pause();
273}
Note: See TracBrowser for help on using the repository browser.