source: Evidence/Evidence.cc@ 175

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