source: fact/Evidence/Evidence.cc@ 12892

Last change on this file since 12892 was 12892, checked in by ogrimm, 13 years ago
New inCallback() method of DIM version v19r23 used
File size: 16.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() and non-blocking by GetConfigNB().
12 - If the configuration file changes a signal can be emitted which is caught by the standard
13 signal handler. The handler invokes ConfigChanged() which can be redefined by the user application.
14 The signal is delivered only to the main thread (where the constructor is executed) and thus
15 blocking rpc can be made from it. The signal is set using ActivateSignal().
16 - Signal handlers to ignore common signals are installed.
17 These signals will then cause pause() to return which can be used
18 by the application to terminate gracefully.
19 - The static method ToString() converts the contents of a DIM service into text
20 - A terminate-handler is installed for catching unhandled C++ exceptions.
21
22 Oliver Grimm
23
24\********************************************************************/
25
26#include "Evidence.h"
27using namespace std;
28
29//
30// Internal configuration class of EvidenceServer
31//
32
33// Constructor: Subscribe to be informed on configuration file change
34EvidenceServer::Config::Config(): DimCommand((This->Name+"/ResetMessage").c_str(), (char *) ""),
35 DimRpcInfo("ConfigRequest", NO_LINK) {
36
37 CurrentItem = string();
38 ConfigTimeStamp = 0;
39 Service = new DimInfo("Config/ModifyTime", NO_LINK, this);
40}
41
42// Destructor
43EvidenceServer::Config::~Config() {
44
45 delete Service;
46}
47
48// Reset message and severity
49void EvidenceServer::Config::commandHandler() {
50
51 This->Message(INFO, "Message reset by %s (ID %d)", getClientName(), getClientId());
52}
53
54// Track last modification time of configuration file
55void EvidenceServer::Config::infoHandler() {
56
57 pthread_t Thread;
58 int Ret;
59
60 if (!ServiceOK(DimInfo::getInfo())) return;
61
62 This->Lock();
63 ConfigTimeStamp = getInfo()->getInt();
64 This->Unlock();
65
66 // Launch ConfigChanged() as detached thread
67 if ((Ret = pthread_create(&Thread, NULL, (void * (*)(void *)) EvidenceServer::CallConfigChanged, (void *) NULL)) != 0) {
68 This->Message(ERROR, "pthread_create() failed in EvidenceServer::Config::infoHandler() (%s)\n", strerror(Ret));
69 }
70 else if ((Ret = pthread_detach(Thread)) != 0) {
71 This->Message(ERROR, "pthread_detach() failed in EvidenceServer::Config::infoHandler() (%s)\n", strerror(Ret));
72 }
73}
74
75// Answer to remote procedure call: Update map
76void EvidenceServer::Config::rpcInfoHandler(){
77
78 This->Lock();
79 This->List[CurrentItem].Value = string(DimRpcInfo::getString(), DimRpcInfo::getSize()-1);
80 This->List[CurrentItem].Time = ConfigTimeStamp;
81
82 // Clear to allow new rpc call
83 CurrentItem.clear();
84 This->Unlock();
85}
86
87
88//////////////////////////
89// EvidenceServer Class //
90//////////////////////////
91
92// Initialise
93EvidenceServer *EvidenceServer::This = NULL;
94pthread_mutex_t EvidenceServer::Mutex;
95
96// Constructor
97EvidenceServer::EvidenceServer(string ServerName): Name(ServerName) {
98
99 // Initialize
100 MessageService = NULL;
101 MessageData = NULL;
102 ExitRequest = false;
103 This = this;
104
105 // Initialise mutex
106 int Ret;
107 pthread_mutexattr_t Attr;
108
109 if ((Ret = pthread_mutexattr_init(&Attr)) != 0) {
110 Message(FATAL, "pthread_mutex_init() failed in Evidence constructor (%s)", strerror(Ret));
111 }
112 if ((Ret = pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
113 Message(FATAL, "pthread_mutex_settype() failed in Evidence constructor (%s)", strerror(Ret));
114 }
115 if ((Ret = pthread_mutex_init(&Mutex, &Attr)) != 0) {
116 Message(FATAL, "pthread_mutex_init() failed in Evidence constructor (%s)", strerror(Ret));
117 }
118
119 // Catch some signals
120 signal(SIGQUIT, &SignalHandler); // CTRL-Backspace
121 signal(SIGTERM, &SignalHandler); // Termination signal
122 signal(SIGINT, &SignalHandler); // CTRL-C
123 signal(SIGHUP, &SignalHandler); // Terminal closed
124
125 // Catch C++ unhandled exceptions
126 set_terminate(Terminate);
127
128 // Message service and initial message
129 MessageService = new DimService((Name+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
130
131 string Rev(EVIDENCE_REVISION);
132 Message(INFO, "Server started (%s, compiled %s %s)", (Rev.substr(1, Rev.size()-3)).c_str(),__DATE__, __TIME__);
133
134 // Configuration class
135 ConfClass = new class Config();
136
137 // Start server
138 start(ServerName.c_str());
139 addExitHandler(this);
140}
141
142
143// Destructor: Free memory
144EvidenceServer::~EvidenceServer() {
145
146 Message(INFO, "Server stopped");
147
148 delete ConfClass;
149
150 int Ret;
151 if ((Ret = pthread_mutex_destroy(&Mutex)) != 0) {
152 Message(ERROR, "pthread_mutex_destroy() failed (%s)", strerror(Ret));
153 }
154
155 delete MessageService;
156 delete[] MessageData;
157}
158
159
160// DIM exit handler
161// Note handler runs in DIM thread, thus exit() will call destructors of static objects in this thread
162void EvidenceServer::exitHandler(int Code) {
163
164 Message(INFO, "Exit handler called (DIM exit code %d)", Code);
165 exit(EXIT_SUCCESS);
166}
167
168// DIM error handler
169void EvidenceServer::errorHandler(int Severity, int Code, char *Text) {
170
171 Message(ERROR, "%s (DIM error code %d, DIM severity %d)\n", Text, Code, Severity);
172}
173
174
175// Set server message (if Severity is FATAL, exit() will be invoked)
176void EvidenceServer::Message(int Severity, const char *Format, ...) {
177
178 static char ErrorString[] = "vasprintf() failed in Message()";
179 char Buffer[10];
180 char *Text;
181
182 // Assemble message from application
183 va_list ArgumentPointer;
184 va_start(ArgumentPointer, Format);
185 if (vasprintf(&Text, Format, ArgumentPointer) == -1) {
186 Text = ErrorString;
187 Severity = ERROR;
188 }
189 va_end(ArgumentPointer);
190
191 // Generate new Message structure and free text
192 struct Message *NewMsg = (struct Message *) new char [sizeof(struct Message)+strlen(Text)+1];
193 NewMsg->Severity = Severity;
194 strcpy(NewMsg->Text, Text);
195 if (Text != ErrorString) free(Text);
196
197 // Send message to console and log file
198 const char *Desc;
199 switch (Severity) {
200 case INFO: Desc = "Info"; break;
201 case WARN: Desc = "Warn"; break;
202 case ERROR: Desc = "Error"; break;
203 case FATAL: Desc = "Fatal"; break;
204 default: snprintf(Buffer, sizeof(Buffer), "%d", Severity);
205 Desc = Buffer;
206 }
207
208 printf("%s (%s): %s\n", MessageService->getName(), Desc, NewMsg->Text);
209 SendToLog("%s (%s): %s", MessageService->getName(), Desc, NewMsg->Text);
210
211 // Update DIM message service
212 if (MessageService != NULL) {
213 MessageService->updateService(NewMsg, sizeof(struct Message)+strlen(NewMsg->Text)+1);
214 }
215
216 // Delete old message
217 Lock();
218 delete[] MessageData;
219 MessageData = NewMsg;
220 Unlock();
221
222 // Terminate if severity if FATAL
223 if (Severity == FATAL) exit(EXIT_FAILURE);
224}
225
226
227// Send to central logging server with non-blocking command
228void EvidenceServer::SendToLog(const char *Format, ...) {
229
230 static char ErrorString[] = "vasprintf() failed in SendToLog()";
231 char *Buffer;
232 int Ret;
233
234 // Evaluate variable argument list
235 va_list ArgumentPointer;
236 va_start(ArgumentPointer, Format);
237 Ret = vasprintf(&Buffer, Format, ArgumentPointer);
238 va_end(ArgumentPointer);
239
240 // Send to logger
241 if (Ret != -1) {
242 DimClient::sendCommandNB("DColl/Log", Buffer);
243 free (Buffer);
244 }
245 else DimClient::sendCommandNB("DColl/Log", ErrorString);
246}
247
248
249// Get configuration data
250//
251// Program terminates if data is missing and no default given. Actual configuration
252// request will be made only if config file has modification since last request.
253// If called from infoHandler(), a non-blocking request will be made
254string EvidenceServer::GetConfig(string Item, string Default) {
255
256 string Result;
257
258 // If up-to-date data in configuration list available, return this
259 Lock();
260 if ((List.count(Item) > 0) && (List[Item].Time >= ConfClass->ConfigTimeStamp)) Result = List[Item].Value;
261 Unlock();
262 if (!Result.empty()) return Result;
263
264 if (inCallback() == 0) {
265 // Blocking configuration request
266 DimRpcInfo Config((char *) "ConfigRequest", NO_LINK);
267 Config.setData((char *) (Name + " " + Item).c_str());
268
269 // Check if successful
270 if (!EvidenceServer::ServiceOK(&Config)) {
271 if (Default.empty()) {
272 Message(FATAL, "Configuration server unreachable, can't retrieve '%s'", Item.c_str());
273 }
274 else Result = Default;
275 }
276 else {
277 if (Config.getSize() <= 1) Result = Default;
278 else Result = string(Config.getString(), Config.getSize()-1); // Retrieve string safely
279 }
280
281 // Update configuration map
282 if (!Result.empty()) {
283 Lock();
284 List[Item].Value = Result;
285 List[Item].Time = ConfClass->ConfigTimeStamp;
286 Unlock();
287 }
288 }
289 else {
290 // Non-blocking configuration request
291 Lock();
292
293 // Make request if currently non is processed
294 if (ConfClass->CurrentItem.empty()) {
295 ConfClass->CurrentItem = Item;
296 ConfClass->setData(((char *) (Name + " " + Item).c_str()));
297 }
298
299 if (List.count(Item) > 0) Result = List[Item].Value;
300 else Result = Default;
301 Unlock();
302 }
303
304 // Terminate if no configuration information found
305 if (Result.empty()) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
306
307 return Result;
308}
309
310
311// Locking and unlocking functions.
312// Message() is not used to avoid infinite recursion
313void EvidenceServer::Lock() {
314
315 int Ret;
316
317 if ((Ret = pthread_mutex_lock(&EvidenceServer::Mutex)) != 0) {
318 printf("pthread_mutex_lock() failed in Lock() (%s)", strerror(Ret));
319 SendToLog("pthread_mutex_lock() failed in Lock() (%s)", strerror(Ret));
320 exit(EXIT_FAILURE);
321 }
322}
323
324void EvidenceServer::Unlock() {
325
326 int Ret;
327
328 if ((Ret = pthread_mutex_unlock(&EvidenceServer::Mutex)) != 0) {
329 printf("pthread_mutex_unlock() failed in Unlock() (%s)", strerror(Ret));
330 SendToLog("pthread_mutex_unlock() failed in Unlock() (%s)", strerror(Ret));
331 exit(EXIT_FAILURE);
332 }
333}
334
335
336// ====== Static methods ======
337
338// Stub to call ConfigChanged() method of class as separate thread
339// Thread set is used to determine if blocking or non-blocking rpc is to be used
340void EvidenceServer::CallConfigChanged() {
341
342 This->ConfigChanged();
343}
344
345
346// Signal handler (causes pause() and other syscalls to return)
347void EvidenceServer::SignalHandler(int Signal) {
348
349 static int Count = 0;
350
351 // At first invocation just request exit
352 if (++Count == 1) {
353 This->ExitRequest = true;
354 return;
355 }
356
357 // If invoked twice, call exit()
358 if (Count == 2) {
359 This->Message(WARN, "Signal handler called twice, invoking exit() (signal %d)", Signal);
360 exit(EXIT_FAILURE);
361 }
362
363 // If invoked more than twice, call abort()
364 This->Message(WARN, "Signal handler called %d times, invoking abort(). Good bye. (signal %d)", Count, Signal);
365 abort();
366}
367
368
369// C++ exception handler (derived from gcc __verbose_terminate_handler())
370void EvidenceServer::Terminate() {
371
372 ostringstream Msg;
373 static bool Terminating = false;
374
375 if (Terminating) {
376 Msg << This->Name << ": Terminate() called recursively, calling abort()";
377 printf("%s\n", Msg.str().c_str());
378 This->SendToLog(Msg.str().c_str());
379 abort();
380 }
381
382 Terminating = true;
383
384 // Make sure there was an exception; terminate is also called for an
385 // attempt to rethrow when there is no suitable exception.
386 type_info *Type = abi::__cxa_current_exception_type();
387 if (Type != NULL) {
388 int Status = -1;
389 char *Demangled = NULL;
390
391 Demangled = abi::__cxa_demangle(Type->name(), 0, 0, &Status);
392 Msg << "Terminate() called after throwing an instance of '" << (Status==0 ? Demangled : Type->name()) << "'";
393 free(Demangled);
394
395 // If exception derived from std::exception, more information.
396 try { __throw_exception_again; }
397 catch (exception &E) {
398 Msg << " (what(): " << E.what() << ")";
399 }
400 catch (...) { }
401 }
402 else Msg << "Terminate() called without an active exception";
403
404 This->Message(FATAL, Msg.str().c_str());
405}
406
407
408// Translates DIM data safely to string (assures no invalid memory accesses are made)
409string EvidenceServer::ToString(char *Format, void *Data, int Size) {
410
411 ostringstream Text;
412
413 // 'Message' service format handled here
414 if (strcmp(Format, "I:1;C") == 0 && Size >= (int) sizeof(struct Message)) {
415 struct Message *Msg = (struct Message *) Data;
416 // Safely extract string and limit to length of C string (padding might make it longer)
417 string MsgText(Msg->Text, Size-sizeof(struct Message));
418 Text << Msg->Severity << " " << MsgText.erase(strlen(MsgText.c_str()));
419
420 return Text.str();
421 }
422
423 // Simplify format "?;1" to "?"
424 if (strlen(Format)==3 && Format[2]=='1') Format[1] = '\0';
425
426 // Structure: print hex representation
427 if (strlen(Format) != 1) {
428 for (int i=0; i<Size; i++) {
429 Text << setw(2) << hex << *((char *) Data + i) << " ";
430 }
431 return Text.str();
432 }
433
434 // String if format "C" and terminated with \0
435 if (strcmp(Format, "C") == 0 && Size > 0 && *((char *) Data+Size-1)=='\0') {
436 return string((char *) Data);
437 }
438
439 // Number array
440 int ElementSize;
441 switch (toupper(*Format)) {
442 case 'B':
443 case 'V':
444 case 'C': ElementSize = sizeof(char); break;
445 case 'I':
446 case 'L': ElementSize = sizeof(int); break;
447 case 'S': ElementSize = sizeof(short); break;
448 case 'F': ElementSize = sizeof(float); break;
449 case 'D': ElementSize = sizeof(double); break;
450 case 'X': ElementSize = sizeof(long long); break;
451 default: return string();
452 }
453
454 for (int i=0; i<Size/ElementSize; i++) {
455 // Space between entries
456 if (i != 0) Text << " ";
457
458 // Translate data
459 switch (toupper(*Format)) {
460 case 'B':
461 case 'V':
462 case 'C': Text << *((char *) Data + i); break;
463 case 'I':
464 case 'L': Text << *((int *) Data + i); break;
465 case 'S': Text << *((short *) Data + i); break;
466 case 'F': Text << *((float *) Data + i); break;
467 case 'D': Text << *((double *) Data + i); break;
468 case 'X': Text << *((long long *) Data + i); break;
469 }
470 }
471
472 return Text.str();
473}
474
475
476// Checks if service contents indicates not available
477bool EvidenceServer::ServiceOK(DimInfo *Item) {
478
479 return !((Item->getSize() == strlen(NO_LINK)+1) &&
480 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
481}
482
483bool EvidenceServer::ServiceOK(DimRpcInfo *Item) {
484
485 return !((Item->getSize() == strlen(NO_LINK)+1) &&
486 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
487}
488
489bool EvidenceServer::ServiceOK(DimCurrentInfo *Item) {
490
491 return !((Item->getSize() == strlen(NO_LINK)+1) &&
492 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
493}
494
495
496// Tokenize std::string using given delimeter list
497vector<string> EvidenceServer::Tokenize(const string &String, const string &Delimiters) {
498
499 vector<string> Tokens;
500 string::size_type Next, EndNext=0;
501
502 while (EndNext != string::npos) {
503 // Find next token
504 Next = String.find_first_not_of(Delimiters, EndNext);
505 EndNext = String.find_first_of(Delimiters, Next);
506
507 // Stop if end of string reached
508 if (Next == string::npos) break;
509
510 // Add token to vector.
511 Tokens.push_back(String.substr(Next, EndNext - Next));
512 }
513
514 return Tokens;
515}
516
517
518///////////////////////////
519// EvidenceHistory Class //
520///////////////////////////
521
522// Organisaztion of history buffer
523// F | T S D | T S D | 0 0 ...... | T S D | T S D | 0 -1
524//
525// F: Offset of oldest entry T: Time S: Size D: Data
526// F, T, S: int
527
528// Marker for history buffer
529const struct EvidenceHistory::Item EvidenceHistory::WrapMark = {0, -1, {}};
530const struct EvidenceHistory::Item EvidenceHistory::EndMark = {0, 0, {}};
531
532// Constructor
533EvidenceHistory::EvidenceHistory(std::string Name): Name(Name) {
534
535 Buffer = NULL;
536}
537
538// Destructor
539EvidenceHistory::~EvidenceHistory() {
540
541 delete[] Buffer;
542}
543
544// Requests service history (returns true if data received OK)
545bool EvidenceHistory::GetHistory() {
546
547 DimRpcInfo R((char *) "ServiceHistory", NO_LINK);
548 R.setData((char *) Name.c_str());
549
550 // Check if data OK
551 if (!EvidenceServer::ServiceOK(&R)) return false;
552 if (R.getSize() == 0) return false;
553
554 // Copy data to buffer
555 delete[] Buffer;
556 BufferSize = R.getSize();
557 Buffer = new char [BufferSize];
558
559 memcpy(Buffer, R.getData(), BufferSize);
560 DataStart = Buffer + strlen(Buffer) + 1;
561 Rewind();
562
563 return true;
564}
565
566// Returns next item in history buffer
567const struct EvidenceHistory::Item *EvidenceHistory::Next() {
568
569 if (Buffer == NULL) return NULL;
570
571 // Check for wrap around
572 if (memcmp(Pointer, &WrapMark, sizeof(WrapMark)) == 0) Pointer = (struct Item *) (DataStart + sizeof(int));
573 // Check if at end of ring buffer
574 if (memcmp(Pointer, &EndMark, sizeof(EndMark)) == 0) return NULL;
575
576 const struct Item *Ret = Pointer;
577 Pointer = (struct Item *) ((char *) (Ret + 1) + Ret->Size);
578
579 return Ret;
580}
581
582// Reset to start of buffer
583void EvidenceHistory::Rewind() {
584
585 if (Buffer != NULL) Pointer = (struct Item *) (DataStart + (*(int *) DataStart));
586}
587
588// Return DIM format string of service (NULL if no data)
589char *EvidenceHistory::GetFormat() {
590
591 return Buffer;
592}
Note: See TracBrowser for help on using the repository browser.