source: fact/Evidence/Evidence.cc@ 12738

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