source: Evidence/Evidence.cc@ 216

Last change on this file since 216 was 216, checked in by ogrimm, 15 years ago
Changed service 'Status' to 'Message' for clarity, added client information in log file entries
File size: 12.2 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 Message(FATAL, "Configuration server unreachable, can't get '%s'", Item.c_str());
177 }
178
179 if (strlen(Result) == 0) {
180 if (Default == NULL) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
181 Result = (char *) Default;
182 }
183
184 // Enlarge list if necessary
185 if (ItemNo == -1) {
186 struct ConfigItem New;
187 ConfigList.push_back(New);
188 ItemNo = ConfigList.size()-1;
189 }
190
191 // Create new entry in item list, allocate memory and copy data to this memory
192 ConfigList[ItemNo].Value = new char [strlen(Result)+1];
193 ConfigList[ItemNo].Name = Item;
194 strcpy(ConfigList[ItemNo].Value, Result);
195 ConfigList[ItemNo].Time = ModifyInfo->LastModifyTime;
196
197 // Return address to configuration value
198 return ConfigList[ItemNo].Value;
199}
200
201
202// ====== Static methods ======
203
204// Signal handler (causes pause() and other syscalls to return)
205void EvidenceServer::SignalHandler(int Signal) {
206
207 static bool Called = false;
208
209 if (!Called) {
210 Called = true;
211 ThisServer->ExitRequest = true;
212 return;
213 }
214
215 // If invoked twice, call exit()
216 ThisServer->Message(ThisServer->WARN, "Signal handler called again, invoking exit() (signal %d)", Signal);
217 exit(EXIT_FAILURE);
218}
219
220// C++ exception handler (derived from gcc __verbose_terminate_handler())
221void EvidenceServer::Terminate() {
222
223 static char Msg[STATUS_SIZE];
224 static bool Terminating = false;
225
226 if (Terminating) {
227 snprintf(Msg, sizeof(Msg), "%s: Terminate() called recursively, calling abort()", ThisServer->Status->getName());
228 printf("%s\n", Msg);
229 DimClient::sendCommandNB("DColl/Log", Msg);
230 abort();
231 }
232
233 Terminating = true;
234
235 // Make sure there was an exception; terminate is also called for an
236 // attempt to rethrow when there is no suitable exception.
237 type_info *Type = abi::__cxa_current_exception_type();
238 if (Type != NULL) {
239 int Status = -1;
240 char *Demangled = NULL;
241
242 Demangled = abi::__cxa_demangle(Type->name(), 0, 0, &Status);
243 snprintf(Msg, sizeof(Msg), "Terminate() called after throwing an instance of '%s'", Status==0 ? Demangled : Type->name());
244 free(Demangled);
245
246 // If exception derived from std::exception, more information.
247 try { __throw_exception_again; }
248 catch (exception &E) {
249 snprintf(Msg+strlen(Msg), sizeof(Msg)-strlen(Msg), " (what(): %s)", E.what());
250 }
251 catch (...) { }
252 }
253 else snprintf(Msg, sizeof(Msg), "Terminate() called without an active exception");
254
255 ThisServer->Message(FATAL, Msg);
256}
257
258
259// Translates DIMInfo to string (memory has to be freed by caller)
260// Static method: it cannot report memory allocation errors via Message()
261char *EvidenceServer::ToString(DimInfo *Item) {
262
263 char *Text;
264
265 // Safety check
266 if (Item->getSize() < 1) return NULL;
267
268 // Structure: print hex representation (3 characters per byte)
269 if (strlen(Item->getFormat()) != 1) {
270 int Size = 3*Item->getSize()+1, N;
271 if ((Text = (char *) malloc(Size)) == NULL) return NULL;
272
273 char *CurrPos = Text;
274 for (int i=0; i<Item->getSize(); i++) {
275 N = snprintf(CurrPos, Size, "%02x ", *((char *) Item->getData() + i));
276 if (N<0 || N>=Size) {
277 free(Text);
278 if (asprintf(&Text, "Structure length %d bytes, buffer overflow in ToString()", Item->getSize()) == -1) return NULL;
279 else return Text;
280 }
281 Size -= N;
282 CurrPos += N;
283 }
284 return Text;
285 }
286
287 // String: terminating \0 is enforced
288 if (toupper(*(Item->getFormat())) == 'C') {
289 *(Item->getString() + Item->getSize() - 1) = '\0';
290 if (asprintf(&Text, "%s", Item->getString()) == -1) return NULL;
291 return Text;
292 }
293
294 // Number array
295 int Size;
296 switch (toupper(*(Item->getFormat()))) {
297 case 'I':
298 case 'L': Size = sizeof(int); break;
299 case 'S': Size = sizeof(short); break;
300 case 'F': Size = sizeof(float); break;
301 case 'D': Size = sizeof(double); break;
302 case 'X': Size = sizeof(long long); break;
303 default: return NULL;
304 }
305
306 int Max, Mem = Item->getSize()*Size*4+1;
307 char *Pos;
308
309 if ((Text = (char *) malloc(Mem)) == NULL) return NULL;
310
311 *Text = '\0';
312 for (int i=0; i<Item->getSize()/Size; i++) {
313 Pos = Text+strlen(Text);
314 Max = Mem-strlen(Text);
315
316 switch (toupper(*(Item->getFormat()))) {
317 case 'I':
318 case 'L': snprintf(Pos, Max, "%d ", *((int *) Item->getData() + i));
319 break;
320 case 'S': snprintf(Pos, Max, "%hd ", *((short *) Item->getData() + i));
321 break;
322 case 'F': snprintf(Pos, Max, "%.5f ", *((float *) Item->getData() + i));
323 break;
324 case 'D': snprintf(Pos, Max, "%.5f ", *((double *) Item->getData() + i));
325 break;
326 case 'X': snprintf(Pos, Max, "%lld ", *((long long *) Item->getData() + i));
327 break;
328 }
329 }
330
331 return Text;
332}
333
334// Checks if service contents indicates not available
335bool EvidenceServer::ServiceOK(DimInfo *Item) {
336
337 return !((Item->getSize() == strlen(NO_LINK)+1) &&
338 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
339}
340
341bool EvidenceServer::ServiceOK(DimRpcInfo *Item) {
342
343 return !((Item->getSize() == strlen(NO_LINK)+1) &&
344 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
345}
346
347///////////////////////////
348// EvidenceHistory Class //
349///////////////////////////
350
351// Organisaztion of history buffer
352// F | T S D | T S D | 0 0 ...... | T S D | T S D | 0 -1
353//
354// F: Offset of oldest entry T: Time S: Size D: Data
355// F, T, S: int
356
357// Marker for history buffer
358const struct EvidenceHistory::Item EvidenceHistory::WrapMark = {0, -1, {}};
359const struct EvidenceHistory::Item EvidenceHistory::EndMark = {0, 0, {}};
360
361// Constructor
362EvidenceHistory::EvidenceHistory(std::string Name): Name(Name) {
363
364 Buffer = NULL;
365}
366
367// Destructor
368EvidenceHistory::~EvidenceHistory() {
369
370 delete[] Buffer;
371}
372
373// Requests service history (returns true if data received OK)
374bool EvidenceHistory::GetHistory() {
375
376 DimRpcInfo R((char *) "ServiceHistory", NO_LINK);
377 R.setData((char *) Name.c_str());
378
379 // Check if data OK
380 if (!EvidenceServer::ServiceOK(&R)) return false;
381 if (R.getSize() == 0) return false;
382
383 // Copy data to buffer
384 delete[] Buffer;
385 BufferSize = R.getSize();
386 Buffer = new char [BufferSize];
387
388 memcpy(Buffer, R.getData(), BufferSize);
389 Rewind();
390
391 return true;
392}
393
394// Returns next item in history buffer
395const struct EvidenceHistory::Item *EvidenceHistory::Next() {
396
397 if (Buffer == NULL) return NULL;
398
399 // Check for wrap around
400 if (memcmp(Pointer, &WrapMark, sizeof(WrapMark)) == 0) Pointer = (struct Item *) (Buffer + 4);
401
402 // Check if at end of ring buffer
403 if (memcmp(Pointer, &EndMark, sizeof(EndMark)) == 0) return NULL;
404
405 const struct Item *Ret = Pointer;
406 Pointer = (struct Item *) ((char *) (Pointer + 1) + Pointer->Size);
407
408 return Ret;
409}
410
411// Reset to start of buffer
412void EvidenceHistory::Rewind() {
413
414 if (Buffer != NULL) Pointer = (struct Item *) (Buffer + (*(int *) Buffer));
415}
Note: See TracBrowser for help on using the repository browser.