source: Evidence/Evidence.cc@ 168

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