source: Evidence/Bridge.cc@ 269

Last change on this file since 269 was 262, checked in by ogrimm, 14 years ago
Removed signaling to invoke ConfigChanged(), now it is run as separate thread. New command '/ResetMessage'
File size: 7.3 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();
53 ~Bridge();
54};
55
56// Constructor (ConfigChanged() is automatically called at start-up)
57Bridge::Bridge(): EvidenceServer(SERVER_NAME) {
58
59 // Initialise
60 ServerList = NULL;
61 GetConfig("cmdallow", " ");
62}
63
64
65// Destructor
66Bridge::~Bridge() {
67
68 Undo();
69}
70
71
72// Undo: Delete all subscriptions and regular expressions
73void Bridge::Undo() {
74
75 while (Map.size() != 0) RemoveService((*Map.begin()).first);
76 delete ServerList;
77 for (int i=0; i<RegEx.size(); i++) regfree(&RegEx[i]);
78 RegEx.clear();
79}
80
81
82// Called by signal handler in case configuration changes and by constructor
83void Bridge::ConfigChanged() {
84
85 static string ExcludeString;
86
87 // Check if configuratiomn changed
88 string NewExcludeString = GetConfig("exclude");
89
90 Lock();
91 if (ExcludeString != NewExcludeString) ExcludeString.swap(NewExcludeString);
92 Unlock();
93 if (ExcludeString == NewExcludeString) return;
94
95 // Compile regular expressions
96 regex_t R;
97 vector<regex_t> RegEx;
98
99 vector<string> Exclude = Tokenize(ExcludeString, " \t");
100 for (int i=0; i<Exclude.size(); i++) {
101 int Ret = regcomp(&R, Exclude[i].c_str(), REG_EXTENDED|REG_NOSUB);
102 if (Ret != 0) {
103 char Err[200];
104 regerror(Ret, &R, Err, sizeof(Err));
105 Message(ERROR, "Error compiling regular expression '%s' (%s)", Exclude[i].c_str(), Err);
106 }
107 else RegEx.push_back(R);
108 }
109
110 // Remove previous data, set new regular expressions and subscribe to top-level server list
111 Lock();
112 Undo();
113 Bridge::RegEx = RegEx;
114 ServerList = new DimInfo((char *) "DIS_DNS/SERVER_LIST", NO_LINK, this);
115 Unlock();
116}
117
118
119// Service subscription and repeating
120void Bridge::infoHandler() {
121
122 DimInfo *I = getInfo();
123
124 // Check if service available
125 if (!ServiceOK(I)) return;
126
127 // If service is DIS_DNS/SERVER_LIST, subscribe to all SERVICE_LIST services
128 if (strcmp(I->getName(), "DIS_DNS/SERVER_LIST") == 0) {
129
130 char *Token = strtok(I->getString(), "+-!@");
131 while (Token != NULL) {
132 if (*I->getString() == '-' || *I->getString() == '!') {
133 RemoveService(string(Token)+"/SERVICE_LIST");
134 }
135 else AddService(string(Token)+"/SERVICE_LIST", (char *) "C", DimSERVICE);
136
137 // Skip server IP address and process ID
138 Token = strtok(NULL, "|");
139 Token = strtok(NULL, "@");
140 }
141 return;
142 }
143
144 // If service is SERVICE_LIST, scan and subscribe/unsubscribe to services
145 if (strstr(I->getName(), "/SERVICE_LIST") != NULL) {
146
147 // Bug fix for empty SERVICE_LIST
148 if (strlen(I->getString()) == 0) {
149 string Tmp(I->getName());
150 RemoveService(I->getName());
151 AddService(Tmp.c_str(), (char *) "C", DimSERVICE);
152 return;
153 }
154
155 char *Format, *Name = strtok(I->getString(), "+-!|");
156 while (Name != NULL) {
157 if ((Format = strtok(NULL, "\n")) != NULL) {
158 // Check if service added or removed/unavailable
159 if (*I->getString() == '-' || *I->getString() == '!') {
160 if (strstr(Format, "|RPC") != NULL) {
161 RemoveService(string(Name)+"/RpcIn");
162 RemoveService(string(Name)+"/RpcOut");
163 }
164 else RemoveService(Name);
165 }
166 else {
167 // Determine type of service
168 if (strstr(Format, "|CMD") != NULL) {
169 *(strstr(Format, "|CMD")) = '\0';
170 AddService(Name, Format, DimCOMMAND);
171 }
172 else if (strstr(Format, "|RPC") != NULL) {
173 *(strstr(Format, "|RPC")) = '\0';
174 if (strchr(Format, ',') != NULL) {
175 *strchr(Format, ',') = '\0';
176 AddService(string(Name)+"/RpcIn", Format, DimCOMMAND);
177 AddService(string(Name)+"/RpcOut", Format+strlen(Format)+1, DimSERVICE);
178 }
179
180 }
181 else {
182 Format[strlen(Format)-1] = '\0';
183 AddService(Name, Format, DimSERVICE);
184 }
185 }
186 }
187 Name = strtok(NULL, "|");
188 }
189 return;
190 }
191
192 // Check if service known and repeat to secondary DNS
193 if (Map.count(I->getName()) == 0) return;
194
195 // Copy service data
196 delete[] Map[I->getName()].Data;
197 Map[I->getName()].Data = new char [I->getSize()];
198 memcpy(Map[I->getName()].Data, I->getData(), I->getSize());
199
200 // Set new service properties and update service
201 Map[I->getName()].Service->setQuality(I->getQuality());
202 Map[I->getName()].Service->setTimestamp(I->getTimestamp(), I->getTimestampMillisecs());
203 Map[I->getName()].Service->updateService(Map[I->getName()].Data, I->getSize());
204}
205
206
207// Command repeating (also handles requests for remote procedure calls)
208void Bridge::commandHandler() {
209
210 // Check if client allowed to send commands
211 vector<string> Client = Tokenize(getClientName(), "@");
212 if (Client.size() == 2 && GetConfig("cmdallow").find(Client[1]) == string::npos) {
213 Message(INFO, "Rejected command/rpc from %s (ID %d)", getClientName(), getClientId());
214 return;
215 }
216
217 // Send command to server
218 sendCommandNB(getCommand()->getName(), getCommand()->getData(), getCommand()->getSize());
219}
220
221
222// Service subscription
223void Bridge::AddService(string Name, char *Format, int Type) {
224
225 // Check if already subscribed to this service
226 if (Map.count(Name) != 0) return;
227
228 // Should service be ignored?
229 for (int i=0; i<RegEx.size(); i++) {
230 if (regexec(&RegEx[i], Name.c_str(), (size_t) 0, NULL, 0) == 0) return;
231 }
232
233 // Create subscription and service to secondary DNS or new command
234 Map[Name].Command = NULL;
235 Map[Name].DataItem = NULL;
236 Map[Name].Service = NULL;
237 Map[Name].Data = NULL;
238
239 if (Type == DimSERVICE) {
240 Map[Name].Service = new DimService(Name.c_str(), (char *) Format, Map[Name].Data, 0);
241 Map[Name].DataItem = new DimStampedInfo(Name.c_str(), NO_LINK, this);
242 }
243 else if (Type == DimCOMMAND) Map[Name].Command = new DimCommand(Name.c_str(), Format, this);
244}
245
246
247// Remove service from watch list (unused pointer are NULL)
248void Bridge::RemoveService(string Name) {
249
250 // Check if actually subscribed to service
251 if (Map.count(Name) == 0) return;
252
253 delete Map[Name].DataItem;
254 delete Map[Name].Service;
255 delete[] Map[Name].Data;
256 delete Map[Name].Command;
257
258 Map.erase(Name);
259}
260
261
262// Main program
263int main(int argc, char *argv[]) {
264
265 // Check command line argument
266 if (argc == 1) {
267 printf("Usage: %s <address of primary DNS> [port] (default port %d)\n", argv[0], DEFAULT_PORT);
268 exit(EXIT_FAILURE);
269 }
270
271 // Set primary DIM network to subscribe from
272 DimClient::setDnsNode(argv[1], argc>2 ? atoi(argv[2]) : DEFAULT_PORT);
273
274 // Static ensures calling of destructor by exit()
275 static Bridge Class;
276
277 // Sleep until signal caught
278 while (!Class.ExitRequest) pause();
279}
Note: See TracBrowser for help on using the repository browser.