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.
|
---|
8 | It can be updated with the Msg() method. The text will also be logged.
|
---|
9 | - Configuration data can be requested by GetConfig().
|
---|
10 | - Signal handlers to ignore common signals are installed.
|
---|
11 | These signals will then cause pause() to return which can be used
|
---|
12 | by the application to terminate gracefully.
|
---|
13 | - The static method ToString() converts the contents of a
|
---|
14 | DIMInfo service into text
|
---|
15 |
|
---|
16 | All memory allocated by the non-static methods will be freed by the
|
---|
17 | class destructor.
|
---|
18 |
|
---|
19 | Oliver Grimm, December 2009
|
---|
20 |
|
---|
21 | \********************************************************************/
|
---|
22 |
|
---|
23 | #include "Evidence.h"
|
---|
24 |
|
---|
25 | bool EvidenceServer::ExitRequest = false;
|
---|
26 |
|
---|
27 | // Constructor starts server with given name
|
---|
28 | EvidenceServer::EvidenceServer(const char *Name) {
|
---|
29 |
|
---|
30 | // Initialize
|
---|
31 | Status = NULL;
|
---|
32 | MsgBuffer = NULL;
|
---|
33 | ConfigList = NULL;
|
---|
34 | ConfigNum = 0;
|
---|
35 |
|
---|
36 | // Catch some signals
|
---|
37 | signal(SIGQUIT, &SignalHandler); // CTRL-Backspace
|
---|
38 | signal(SIGTERM, &SignalHandler); // Termination signal
|
---|
39 | signal(SIGINT, &SignalHandler); // CTRL-C
|
---|
40 | signal(SIGHUP, &SignalHandler); // Terminal closed
|
---|
41 |
|
---|
42 | // Create server name
|
---|
43 | if (asprintf(&ServerName, "%s/Status", Name) == -1) {
|
---|
44 | ServerName = NULL;
|
---|
45 | Msg(FATAL, "Could not generate service name, asprintf() failed");
|
---|
46 | }
|
---|
47 |
|
---|
48 | // Start server
|
---|
49 | static char InitMsg[] = "OK" "\0" "0";
|
---|
50 | Status = new DimService(ServerName, (char *) "C", InitMsg, sizeof(InitMsg));
|
---|
51 | DimServer::start(Name);
|
---|
52 | DimServer::addExitHandler(this);
|
---|
53 |
|
---|
54 | Msg(INFO, "Server started");
|
---|
55 | }
|
---|
56 |
|
---|
57 | // Destructor: Frees all buffers
|
---|
58 | EvidenceServer::~EvidenceServer() {
|
---|
59 |
|
---|
60 | free(ServerName);
|
---|
61 | free(MsgBuffer);
|
---|
62 |
|
---|
63 | for (unsigned int i=0; i<ConfigNum; i++) {
|
---|
64 | delete *((DimRpcInfo **) ConfigList + i);
|
---|
65 | }
|
---|
66 | free(ConfigList);
|
---|
67 | }
|
---|
68 |
|
---|
69 | // DIM exit handler
|
---|
70 | void EvidenceServer::exitHandler(int Code) {
|
---|
71 | Msg(INFO, "Server stopped (DIM exit code %d)", Code);
|
---|
72 | exit(EXIT_SUCCESS);
|
---|
73 | }
|
---|
74 |
|
---|
75 | // DIM error handler
|
---|
76 | void EvidenceServer::errorHandler(int Severity, int Code, char *Message) {
|
---|
77 | Msg(ERROR, "%s (DIM Error, code %d, severity %d)\n", Message, Code, Severity);
|
---|
78 | }
|
---|
79 |
|
---|
80 | // Print message to screen, to log and to status
|
---|
81 | //
|
---|
82 | // The message format is special: after the string-terminating '\0'
|
---|
83 | // the Severity is given, terminated by another '\0'
|
---|
84 | // The buffer must have the same lifetime as the DIM service.
|
---|
85 | // If Severity is FATAL, exit() will be invoked.
|
---|
86 | void EvidenceServer::Msg(MsgType Severity, const char *Format, ...) {
|
---|
87 |
|
---|
88 | char *TmpBuffer;
|
---|
89 | int Size;
|
---|
90 |
|
---|
91 | static char DefaultMsg[]="vasprintf() or asprintf() failed, cannot process message" "\0" "2"; // Severity 'Error'
|
---|
92 |
|
---|
93 | // Assemble message from application
|
---|
94 | va_list ArgumentPointer;
|
---|
95 | va_start(ArgumentPointer, Format);
|
---|
96 | if (vasprintf(&TmpBuffer, Format, ArgumentPointer) == -1) TmpBuffer = NULL;
|
---|
97 | va_end(ArgumentPointer);
|
---|
98 |
|
---|
99 | // Free previous message buffer if allocated dynamically
|
---|
100 | if (MsgBuffer != DefaultMsg) free(MsgBuffer);
|
---|
101 |
|
---|
102 | // Assemble final message including headers and Severity encoding
|
---|
103 | if (TmpBuffer == NULL) Size = -1;
|
---|
104 | else Size = asprintf(&MsgBuffer, "%s: %s%s*%d", ServerName,
|
---|
105 | (Severity==FATAL) ? "(Fatal) ":"", TmpBuffer,
|
---|
106 | (unsigned char) Severity);
|
---|
107 | free(TmpBuffer);
|
---|
108 |
|
---|
109 | // If memory could not be allocated, show default message,
|
---|
110 | // otherwise replace '*' by '0' for severity encoding
|
---|
111 | if (Size == -1) {
|
---|
112 | MsgBuffer = DefaultMsg;
|
---|
113 | Size = sizeof(DefaultMsg) - 1;
|
---|
114 | }
|
---|
115 | else *(strrchr(MsgBuffer, '*')) = '\0';
|
---|
116 |
|
---|
117 | // Send message to console, log file and update DIM status service
|
---|
118 | printf("%s\n", MsgBuffer);
|
---|
119 | DimClient::sendCommand("DColl/Log", MsgBuffer);
|
---|
120 | if (Status != NULL) Status->updateService(MsgBuffer, Size+1);
|
---|
121 |
|
---|
122 | // Terminate if message type is fatal
|
---|
123 | if (Severity == FATAL) exit(EXIT_FAILURE);
|
---|
124 | }
|
---|
125 |
|
---|
126 | // Get configuration data (program terminates if data is missing)
|
---|
127 | //
|
---|
128 | // The memory allocated by all calls to this function will be freed by
|
---|
129 | // the destructor.
|
---|
130 | char* EvidenceServer::GetConfig(const char *Item) {
|
---|
131 |
|
---|
132 | // Enlarge memory to hold new pointer
|
---|
133 | DimRpcInfo **Config = (DimRpcInfo **) realloc(ConfigList, sizeof(void *) * (++ConfigNum));
|
---|
134 | if (Config == NULL) {
|
---|
135 | Msg(WARN, "Could not realloc() memory for DimRpcInfo list (%s)", strerror(errno));
|
---|
136 | ConfigNum--;
|
---|
137 | }
|
---|
138 | else ConfigList = (void *) Config;
|
---|
139 |
|
---|
140 | // Advance array pointer to position for new DimRpcInfo pointer
|
---|
141 | Config += ConfigNum - 1;
|
---|
142 |
|
---|
143 | // Make configuration request
|
---|
144 | *Config = new DimRpcInfo((char *) "ConfigRequest", (char *) "");
|
---|
145 | (*Config)->setData((char *) Item);
|
---|
146 |
|
---|
147 | // Terminate if not successful
|
---|
148 | if (strlen((*Config)->getString()) == 0) {
|
---|
149 | Msg(FATAL, "Missing configuration data '%s'", Item);
|
---|
150 | }
|
---|
151 |
|
---|
152 | return (*Config)->getString();
|
---|
153 | }
|
---|
154 |
|
---|
155 |
|
---|
156 | // ====== Static methods ======
|
---|
157 |
|
---|
158 | // Signal handler (causes pause() to return)
|
---|
159 | void EvidenceServer::SignalHandler(int) {
|
---|
160 | EvidenceServer::ExitRequest = true;
|
---|
161 | }
|
---|
162 |
|
---|
163 | // Translates DIMInfo to string (memory has to be freed by caller)
|
---|
164 | char *EvidenceServer::ToString(DimInfo *Item) {
|
---|
165 |
|
---|
166 | char *Text;
|
---|
167 | int R;
|
---|
168 |
|
---|
169 | // Cannot handle complex data formats
|
---|
170 | if (strlen(Item->getFormat()) != 1) return NULL;
|
---|
171 |
|
---|
172 | // Interpret format
|
---|
173 | switch (*(Item->getFormat())) {
|
---|
174 | case 'I': R = asprintf(&Text, "%d", Item->getInt()); break;
|
---|
175 | case 'C': R = asprintf(&Text, "%s", Item->getString()); break;
|
---|
176 | case 'S': R = asprintf(&Text, "%hd", Item->getShort()); break;
|
---|
177 | case 'F': R = asprintf(&Text, "%.3f", Item->getFloat()); break;
|
---|
178 | case 'D': R = asprintf(&Text, "%.3f", Item->getDouble()); break;
|
---|
179 | case 'X': R = asprintf(&Text, "%lld", Item->getLonglong()); break;
|
---|
180 | default: return NULL;
|
---|
181 | }
|
---|
182 |
|
---|
183 | // Check if aprintf() worked OK
|
---|
184 | if (R != -1) return Text;
|
---|
185 | else return NULL;
|
---|
186 | }
|
---|