source: Evidence/Evidence.cc@ 163

Last change on this file since 163 was 159, checked in by ogrimm, 15 years ago
Change in history handling
File size: 8.6 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
18 All memory allocated by the non-static methods will be freed by the
19 class destructor.
20
21 Oliver Grimm, December 2009
22
23\********************************************************************/
24
25#include "Evidence.h"
26
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
43 // Catch some signals
44 signal(SIGQUIT, &SignalHandler); // CTRL-Backspace
45 signal(SIGTERM, &SignalHandler); // Termination signal
46 signal(SIGINT, &SignalHandler); // CTRL-C
47 signal(SIGHUP, &SignalHandler); // Terminal closed
48
49 // Catch C++ unhandled exceptions
50 set_terminate(Terminate);
51
52 // Start server
53 static char Init[] = "Server started";
54 Status = new DimService((string(Name) + "/Status").c_str(), (char *) "C", Init, sizeof(Init));
55
56 start(Name);
57 addExitHandler(this);
58}
59
60// Destructor: Free memory
61EvidenceServer::~EvidenceServer() {
62
63 State(INFO, "Server stopped");
64
65 for (unsigned int i=0; i<ConfigList.size(); i++) {
66 delete[] ConfigList[i].Name;
67 delete[] ConfigList[i].Value;
68 }
69}
70
71// DIM exit handler
72void EvidenceServer::exitHandler(int Code) {
73 State(INFO, "Exit handler called (DIM exit code %d)", Code);
74 exit(EXIT_SUCCESS);
75}
76
77// DIM error handler
78void EvidenceServer::errorHandler(int Severity, int Code, char *Message) {
79 State(ERROR, "%s (DIM error code %d, severity %d)\n", Message, Code, Severity);
80}
81
82// Set status of server
83//
84// The message format is special: after the string-terminating '\0' the Severity
85// is given, terminated by another '\0' The buffer for the DIM service must have
86// the same lifetime as the DIM service. If Severity is FATAL, exit() will be invoked.
87void EvidenceServer::State(StateType Severity, const char *Format, ...) {
88
89 static const char* StateString[] = {"Info", "Warn", "Error", "Fatal"};
90 static char ErrorString[] = "vasprintf() failed in State()";
91 static char SBuf[STATUS_SIZE];
92 char TBuf[STATUS_SIZE];
93 char *Tmp;
94
95 // Assemble message from application
96 va_list ArgumentPointer;
97 va_start(ArgumentPointer, Format);
98 if (vasprintf(&Tmp, Format, ArgumentPointer) == -1) Tmp = ErrorString;
99 va_end(ArgumentPointer);
100
101 // Create normal string
102 snprintf(TBuf, sizeof(TBuf), "%s (%s): %s", Status->getName(), StateString[Severity], Tmp);
103
104 // Create string with severity encoding
105 snprintf(SBuf, sizeof(SBuf), "%s**", Tmp);
106 SBuf[strlen(SBuf)-2] = '\0';
107 SBuf[strlen(SBuf)+1] = Severity;
108
109 if (Tmp != ErrorString) free(Tmp);
110
111 // Send message to console and log file
112 printf("%s\n", TBuf);
113 DimClient::sendCommandNB("DColl/Log", TBuf);
114
115 // Update DIM status service (including severity encoding)
116 if (Status != NULL) Status->updateService(SBuf, strlen(SBuf)+2);
117
118 // Terminate if message type is fatal
119 if (Severity == FATAL) exit(EXIT_FAILURE);
120}
121
122// Get configuration data (program terminates if data is missing)
123//
124// The memory allocated by all calls to this function will be freed by
125// the destructor.
126char* EvidenceServer::GetConfig(const char *Item, const char *Default) {
127
128 // Determine configuration file update time
129 DimCurrentInfo ModifyTime("Config/ModifyTime", 0);
130 int Time = ModifyTime.getInt(), ItemNo = -1;
131
132 // Check if configuration request already in list
133 for (unsigned int i=0; i<ConfigList.size(); i++) {
134 if (strcmp(ConfigList[i].Name, Item) == 0) {
135 // Return original value if still up to date
136 if (ConfigList[i].Time >= Time) return ConfigList[i].Value;
137
138 // Otherwise, free memory of old value
139 delete[] ConfigList[i].Name;
140 delete[] ConfigList[i].Value;
141 ItemNo = i;
142 break;
143 }
144 }
145
146 // Make configuration request
147 DimRpcInfo Config((char *) "ConfigRequest", (char *) "");
148 Config.setData((char *) Item);
149 char *Result = Config.getString();
150
151 // Terminate if not successful
152 if (strlen(Result) == 0) {
153 if (Default == NULL) State(FATAL, "Missing configuration data '%s'", Item);
154 Result = (char *) Default;
155 }
156
157 // Enlarge list if necessary
158 if (ItemNo == -1) {
159 struct ConfigItem New;
160 ConfigList.push_back(New);
161 ItemNo = ConfigList.size()-1;
162 }
163
164 // Create new entry in item list, allocate memory and copy data to this memory
165 ConfigList[ItemNo].Value = new char [strlen(Result)+1];
166 ConfigList[ItemNo].Name = new char [strlen(Item)+1];
167 strcpy(ConfigList[ItemNo].Name, Item);
168 strcpy(ConfigList[ItemNo].Value, Result);
169 ConfigList[ItemNo].Time = Time;
170
171 // Return address to configuration value
172 return ConfigList[ItemNo].Value;
173}
174
175
176// ====== Static methods ======
177
178// Signal handler (causes pause() and other syscalls to return)
179void EvidenceServer::SignalHandler(int) {
180
181 ThisServer->ExitRequest = true;
182}
183
184// C++ exception handler
185void EvidenceServer::Terminate() {
186
187 string Msg = string(ThisServer->Status->getName()) + ": Caught unhandled exception";
188
189 printf("%s\n", Msg.c_str());
190 DimClient::sendCommand("DColl/Log", Msg.c_str());
191
192 abort();
193}
194
195
196// Translates DIMInfo to string (memory has to be freed by caller)
197// DIM structures are converted to hexadecimal representation
198// For string conversion, a terminating \0 is enforced.
199char *EvidenceServer::ToString(DimInfo *Item) {
200
201 char *Text;
202 int R=0;
203
204 if (strlen(Item->getFormat()) != 1) {
205 if ((Text = (char *) malloc(3*Item->getSize()+1)) != NULL) {
206 for (int i=0; i<Item->getSize(); i++) sprintf(Text+3*i, "%02x", *((char *) Item->getData() + i));
207 }
208 }
209 else {
210 switch (*(Item->getFormat())) {
211 case 'I': R = asprintf(&Text, "%d", Item->getInt()); break;
212 case 'S': R = asprintf(&Text, "%hd", Item->getShort()); break;
213 case 'F': R = asprintf(&Text, "%.5f", Item->getFloat()); break;
214 case 'D': R = asprintf(&Text, "%.5f", Item->getDouble()); break;
215 case 'X': R = asprintf(&Text, "%lld", Item->getLonglong()); break;
216 case 'C': *(Item->getString() + Item->getSize() - 1) = '\0';
217 R = asprintf(&Text, "%s", Item->getString());
218 break;
219 default: return NULL;
220 }
221 }
222
223 return (R == -1) ? NULL : Text;
224}
225
226// Checks if service contents indicates not available
227bool EvidenceServer::ServiceOK(DimInfo *Item) {
228
229 return !((Item->getSize() == strlen(NO_LINK)+1) &&
230 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
231
232}
233
234
235///////////////////////////
236// EvidenceHistory Class //
237///////////////////////////
238
239// Organisaztion of history buffer
240// F | T S D | T S D | 0 0 ...... | T S D | T S D | 0 -1
241//
242// F: Offset of oldest entry T: Time S: Size D: Data
243// F, T, S: int
244
245// Marker for history buffer
246const int EvidenceHistory::WrapMark[] = {0, -1};
247const int EvidenceHistory::EndMark[] = {0, 0};
248
249// Constructor
250EvidenceHistory::EvidenceHistory() {
251
252 Buffer = NULL;
253}
254
255// Destructor
256EvidenceHistory::~EvidenceHistory() {
257
258 delete[] Buffer;
259}
260
261// Requests service history
262bool EvidenceHistory::GetHistory(char *Name) {
263
264 DimCurrentInfo Info((string(Name)+".hist").c_str(), NO_LINK);
265
266 // Check if service available
267 if (((Info.getSize() == strlen(NO_LINK)+1) &&
268 (memcmp(Info.getData(), NO_LINK, Info.getSize()) == 0))) return false;
269
270 delete[] Buffer;
271 BufferSize = Info.getSize();
272 Buffer = new char [BufferSize];
273
274 memcpy(Buffer, Info.getData(), BufferSize);
275 Offset = *(int *) Buffer;
276
277 return true;
278}
279
280// Returns next item in history buffer
281bool EvidenceHistory::Next(int &Time, int &Size, void *&Data) {
282
283 if (Buffer == NULL) return false;
284
285 // Check for wrap around
286 if (memcmp(Buffer+Offset, WrapMark, sizeof(WrapMark)) == 0) Offset = 4;
287
288 // Check if at end of ring buffer
289 if (memcmp(Buffer+Offset, EndMark, sizeof(EndMark)) == 0) return false;
290
291 Time = *(int *) (Buffer + Offset);
292 Size = *(int *) (Buffer + Offset + sizeof(int));
293 Data = Buffer + Offset + 2*sizeof(int);
294
295 Offset += *((int *) (Buffer + Offset) + 1) + 2*sizeof(int);
296
297 return true;
298}
Note: See TracBrowser for help on using the repository browser.