source: Evidence/Evidence.cc@ 208

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