source: Evidence/Evidence.cc@ 227

Last change on this file since 227 was 224, checked in by ogrimm, 16 years ago
Message severity encoded now using standard DIM structure, other updates
File size: 12.7 KB
Line 
1/********************************************************************\
2
3 General code to start a server of the Evidence Control System
4
5 - The server is started with the given name.
6 - DIM exit and error handlers are implemented.
7 - A Message service is published with severity encoding. It can be updated
8 with the Message() method. The text will also be logged.
9 - If the severity of a Message() call is FATAL, exit() will be called (with
10 this severity, the call to Message() is guranteed not to return).
11 - Configuration data can be requested by GetConfig().
12 - Signal handlers to ignore common signals are installed.
13 These signals will then cause pause() to return which can be used
14 by the application to terminate gracefully.
15 - The static method ToString() converts the contents of a
16 DIMInfo service into text
17 - A terminate-handler is installed for catching unhandled C++ exceptions.
18
19 Memory allocated by the non-static methods will be freed by the
20 class destructor.
21
22 Oliver Grimm, June 2010
23
24\********************************************************************/
25
26#include "Evidence.h"
27using namespace std;
28
29//////////////////////////
30// EvidenceServer Class //
31//////////////////////////
32
33EvidenceServer *ThisServer;
34
35// Constructor starts server with given name
36EvidenceServer::EvidenceServer(const char *Name) {
37
38 // Initialize
39 MessageService = NULL;
40 MessageData = NULL;
41 ExitRequest = false;
42 ThisServer = this;
43 ServerName = Name;
44
45 // Catch some signals
46 signal(SIGQUIT, &SignalHandler); // CTRL-Backspace
47 signal(SIGTERM, &SignalHandler); // Termination signal
48 signal(SIGINT, &SignalHandler); // CTRL-C
49 signal(SIGHUP, &SignalHandler); // Terminal closed
50
51 // Catch C++ unhandled exceptions
52 set_terminate(Terminate);
53
54 // Subscribe to modify service for keeping track of config file changes
55 ModifyInfo = new class ConfigUpdate();
56
57 // Message service and initial message
58 MessageService = new DimService((ServerName+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
59
60 string Rev(EVIDENCE_REVISION);
61 Message(INFO, "Server started (%s, compiled %s %s)", (Rev.substr(1, Rev.size()-3)).c_str(),__DATE__, __TIME__);
62
63 // Start server
64 start(Name);
65 addExitHandler(this);
66}
67
68// Destructor: Free memory
69EvidenceServer::~EvidenceServer() {
70
71 Message(INFO, "Server stopped");
72
73 for (unsigned int i=0; i<ConfigList.size(); i++) delete[] ConfigList[i].Value;
74 delete ModifyInfo;
75 delete MessageService;
76 delete MessageData;
77}
78
79// DIM exit handler
80void EvidenceServer::exitHandler(int Code) {
81
82 Message(INFO, "Exit handler called (DIM exit code %d)", Code);
83 exit(EXIT_SUCCESS);
84}
85
86// DIM error handler
87void EvidenceServer::errorHandler(int Severity, int Code, char *Text) {
88 Message(ERROR, "%s (DIM error code %d, DIM severity %d)\n", Text, Code, Severity);
89}
90
91// Set server message (if Severity is FATAL, exit() will be invoked)
92void EvidenceServer::Message(MessageType Severity, const char *Format, ...) {
93
94 static const char* StateString[] = {"Info", "Warn", "Error", "Fatal"};
95 static char ErrorString[] = "vasprintf() failed in Message()";
96 char *Text;
97
98 // Assemble message from application
99 va_list ArgumentPointer;
100 va_start(ArgumentPointer, Format);
101 if (vasprintf(&Text, Format, ArgumentPointer) == -1) {
102 Text = ErrorString;
103 Severity = ERROR;
104 }
105 va_end(ArgumentPointer);
106
107 // Generate new Message structure and free text
108 struct Message *NewMsg = (struct Message *) new char [sizeof(struct Message)+strlen(Text)+1];
109 NewMsg->Severity = Severity;
110 strcpy(NewMsg->Text, Text);
111 if (Text != ErrorString) free(Text);
112
113 // Send message to console and log file
114 printf("%s (%s): %s\n", MessageService->getName(), StateString[Severity], NewMsg->Text);
115 SendToLog("%s (%s): %s", MessageService->getName(), StateString[Severity], NewMsg->Text);
116
117 // Update DIM message service, then delete old message
118 if (MessageService != NULL) {
119 MessageService->updateService(NewMsg, sizeof(struct Message)+strlen(NewMsg->Text)+1);
120 }
121 delete MessageData;
122 MessageData = NewMsg;
123
124 // Terminate if severity if FATAL
125 if (Severity == FATAL) exit(EXIT_FAILURE);
126}
127
128
129// Set to central logging server with non-blocking command (may be used in DIM handler)
130void EvidenceServer::SendToLog(const char *Format, ...) {
131
132 char *Buffer;
133 int Ret;
134
135 // Evaluate variable argument list
136 va_list ArgumentPointer;
137 va_start(ArgumentPointer, Format);
138 Ret = vasprintf(&Buffer, Format, ArgumentPointer);
139 va_end(ArgumentPointer);
140
141 // Send to logger
142 if (Ret != -1) {
143 DimClient::sendCommandNB("DColl/Log", Buffer);
144 free (Buffer);
145 }
146 else Message(ERROR, "Could not create logging text in SendToLog(), vasprintf() failed");
147}
148
149
150// Get configuration data
151//
152// Program terminates if data is missing and no default given. Actual configuration
153// request will be made only if config file has modification since last request.
154// The memory allocated by all calls to this function will be freed by
155// the destructor.
156char* EvidenceServer::GetConfig(string Item, const char *Default) {
157
158 int ItemNo = -1;
159
160 // Check if configuration request already in list
161 for (unsigned int i=0; i<ConfigList.size(); i++) if (ConfigList[i].Name == Item) {
162 // Return original value if still up to date
163 if (ConfigList[i].Time >= ModifyInfo->LastModifyTime) return ConfigList[i].Value;
164
165 // Otherwise, free memory of old value
166 delete[] ConfigList[i].Value;
167 ItemNo = i;
168 break;
169 }
170
171 // Make configuration request
172 DimRpcInfo Config((char *) "ConfigRequest", NO_LINK);
173 Config.setData((char *) (ServerName + " " + Item).c_str());
174 char *Result = Config.getString();
175
176 // Terminate if not successful
177 if (!EvidenceServer::ServiceOK(&Config)) {
178 if (Default == NULL) Message(FATAL, "Configuration server unreachable, can't get '%s'", Item.c_str());
179 Result = (char *) Default;
180 }
181
182 if (strlen(Result) == 0) {
183 if (Default == NULL) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
184 Result = (char *) Default;
185 }
186
187 // Enlarge list if necessary
188 if (ItemNo == -1) {
189 struct ConfigItem New;
190 ConfigList.push_back(New);
191 ItemNo = ConfigList.size()-1;
192 }
193
194 // Create new entry in item list, allocate memory and copy data to this memory
195 ConfigList[ItemNo].Name = Item;
196 ConfigList[ItemNo].Value = new char [strlen(Result)+1];
197 strcpy(ConfigList[ItemNo].Value, Result);
198 ConfigList[ItemNo].Time = ModifyInfo->LastModifyTime;
199
200 // Return address to configuration value
201 return ConfigList[ItemNo].Value;
202}
203
204
205// ====== Static methods ======
206
207// Signal handler (causes pause() and other syscalls to return)
208void EvidenceServer::SignalHandler(int Signal) {
209
210 static bool Called = false;
211
212 // At first invocation just request exit
213 if (!Called) {
214 Called = true;
215 ThisServer->ExitRequest = true;
216 return;
217 }
218
219 // If invoked twice, call exit()
220 ThisServer->Message(ThisServer->WARN, "Signal handler called again, invoking exit() (signal %d)", Signal);
221 exit(EXIT_FAILURE);
222}
223
224// C++ exception handler (derived from gcc __verbose_terminate_handler())
225void EvidenceServer::Terminate() {
226
227 static char Msg[STATUS_SIZE];
228 static bool Terminating = false;
229
230 if (Terminating) {
231 snprintf(Msg, sizeof(Msg), "%s: Terminate() called recursively, calling abort()", ThisServer->MessageService->getName());
232 printf("%s\n", Msg);
233 DimClient::sendCommandNB("DColl/Log", Msg);
234 abort();
235 }
236
237 Terminating = true;
238
239 // Make sure there was an exception; terminate is also called for an
240 // attempt to rethrow when there is no suitable exception.
241 type_info *Type = abi::__cxa_current_exception_type();
242 if (Type != NULL) {
243 int Status = -1;
244 char *Demangled = NULL;
245
246 Demangled = abi::__cxa_demangle(Type->name(), 0, 0, &Status);
247 snprintf(Msg, sizeof(Msg), "Terminate() called after throwing an instance of '%s'", Status==0 ? Demangled : Type->name());
248 free(Demangled);
249
250 // If exception derived from std::exception, more information.
251 try { __throw_exception_again; }
252 catch (exception &E) {
253 snprintf(Msg+strlen(Msg), sizeof(Msg)-strlen(Msg), " (what(): %s)", E.what());
254 }
255 catch (...) { }
256 }
257 else snprintf(Msg, sizeof(Msg), "Terminate() called without an active exception");
258
259 ThisServer->Message(FATAL, Msg);
260}
261
262
263// Translates DIMInfo to string (memory has to be freed by caller)
264// Static method, cannot report memory allocation errors via Message() but returns NULL
265char *EvidenceServer::ToString(DimInfo *Item) {
266
267 char *Text;
268
269 // Safety check
270 if (Item->getSize() < 1) return NULL;
271
272 // Message structure format handled
273 if (strcmp(Item->getFormat(), "I:1;C") == 0) {
274 struct Message *Msg = (struct Message *) Item->getData();
275 if (asprintf(&Text, "%d %s", Msg->Severity, Msg->Text) == -1) return NULL;
276 else return Text;
277 }
278
279 // Structure: print hex representation (3 characters per byte)
280 if (strlen(Item->getFormat()) != 1) {
281 int Size = 3*Item->getSize()+1, N;
282 if ((Text = (char *) malloc(Size)) == NULL) return NULL;
283
284 char *CurrPos = Text;
285 for (int i=0; i<Item->getSize(); i++) {
286 N = snprintf(CurrPos, Size, "%02x ", *((char *) Item->getData() + i));
287 if (N<0 || N>=Size) {
288 free(Text);
289 if (asprintf(&Text, "Structure length %d bytes, buffer overflow in ToString()", Item->getSize()) == -1) return NULL;
290 else return Text;
291 }
292 Size -= N;
293 CurrPos += N;
294 }
295 return Text;
296 }
297
298 // String: terminating \0 is enforced
299 if (toupper(*(Item->getFormat())) == 'C') {
300 *(Item->getString() + Item->getSize() - 1) = '\0';
301 if (asprintf(&Text, "%s", Item->getString()) == -1) return NULL;
302 return Text;
303 }
304
305 // Number array
306 int Size;
307 switch (toupper(*(Item->getFormat()))) {
308 case 'I':
309 case 'L': Size = sizeof(int); break;
310 case 'S': Size = sizeof(short); break;
311 case 'F': Size = sizeof(float); break;
312 case 'D': Size = sizeof(double); break;
313 case 'X': Size = sizeof(long long); break;
314 default: return NULL;
315 }
316
317 int Max, Mem = Item->getSize()*Size*4+1;
318 char *Pos;
319
320 if ((Text = (char *) malloc(Mem)) == NULL) return NULL;
321
322 *Text = '\0';
323 for (int i=0; i<Item->getSize()/Size; i++) {
324 Pos = Text+strlen(Text);
325 Max = Mem-strlen(Text);
326
327 switch (toupper(*(Item->getFormat()))) {
328 case 'I':
329 case 'L': snprintf(Pos, Max, "%d ", *((int *) Item->getData() + i));
330 break;
331 case 'S': snprintf(Pos, Max, "%hd ", *((short *) Item->getData() + i));
332 break;
333 case 'F': snprintf(Pos, Max, "%.5f ", *((float *) Item->getData() + i));
334 break;
335 case 'D': snprintf(Pos, Max, "%.5f ", *((double *) Item->getData() + i));
336 break;
337 case 'X': snprintf(Pos, Max, "%lld ", *((long long *) Item->getData() + i));
338 break;
339 }
340 }
341
342 return Text;
343}
344
345// Checks if service contents indicates not available
346bool EvidenceServer::ServiceOK(DimInfo *Item) {
347
348 return !((Item->getSize() == strlen(NO_LINK)+1) &&
349 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
350}
351
352bool EvidenceServer::ServiceOK(DimRpcInfo *Item) {
353
354 return !((Item->getSize() == strlen(NO_LINK)+1) &&
355 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
356}
357
358///////////////////////////
359// EvidenceHistory Class //
360///////////////////////////
361
362// Organisaztion of history buffer
363// F | T S D | T S D | 0 0 ...... | T S D | T S D | 0 -1
364//
365// F: Offset of oldest entry T: Time S: Size D: Data
366// F, T, S: int
367
368// Marker for history buffer
369const struct EvidenceHistory::Item EvidenceHistory::WrapMark = {0, -1, {}};
370const struct EvidenceHistory::Item EvidenceHistory::EndMark = {0, 0, {}};
371
372// Constructor
373EvidenceHistory::EvidenceHistory(std::string Name): Name(Name) {
374
375 Buffer = NULL;
376}
377
378// Destructor
379EvidenceHistory::~EvidenceHistory() {
380
381 delete[] Buffer;
382}
383
384// Requests service history (returns true if data received OK)
385bool EvidenceHistory::GetHistory() {
386
387 DimRpcInfo R((char *) "ServiceHistory", NO_LINK);
388 R.setData((char *) Name.c_str());
389
390 // Check if data OK
391 if (!EvidenceServer::ServiceOK(&R)) return false;
392 if (R.getSize() == 0) return false;
393
394 // Copy data to buffer
395 delete[] Buffer;
396 BufferSize = R.getSize();
397 Buffer = new char [BufferSize];
398
399 memcpy(Buffer, R.getData(), BufferSize);
400 DataStart = Buffer + strlen(Buffer) + 1;
401 Rewind();
402
403 return true;
404}
405
406// Returns next item in history buffer
407const struct EvidenceHistory::Item *EvidenceHistory::Next() {
408
409 if (Buffer == NULL) return NULL;
410
411 // Check for wrap around
412 if (memcmp(Pointer, &WrapMark, sizeof(WrapMark)) == 0) Pointer = (struct Item *) (DataStart + sizeof(int));
413 // Check if at end of ring buffer
414 if (memcmp(Pointer, &EndMark, sizeof(EndMark)) == 0) return NULL;
415
416 const struct Item *Ret = Pointer;
417 Pointer = (struct Item *) ((char *) (Ret + 1) + Ret->Size);
418
419 return Ret;
420}
421
422// Reset to start of buffer
423void EvidenceHistory::Rewind() {
424
425 if (Buffer != NULL) Pointer = (struct Item *) (DataStart + (*(int *) DataStart));
426}
427
428// Return DIM format string of service (NULL if no data)
429char *EvidenceHistory::GetFormat() {
430
431 return Buffer;
432}
Note: See TracBrowser for help on using the repository browser.