source: Evidence/Bridge.cc@ 686

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