source: Evidence/Evidence.cc@ 223

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