source: Evidence/DColl.cc@ 228

Last change on this file since 228 was 224, checked in by ogrimm, 14 years ago
Message severity encoded now using standard DIM structure, other updates
File size: 10.6 KB
Line 
1/********************************************************************\
2
3 Central data collector of the Evidence Control System
4
5 - DColl subscribes to all services given by the configuration
6 server and writes these to the data file at every update.
7 - One data file per day is generated.
8 - The command 'DColl/Log' writes the associated string to the log file
9
10 Oliver Grimm, June 2010
11
12\********************************************************************/
13
14#define SERVER_NAME "DColl"
15#define LOG_FILENAME "Evidence.log"
16
17#include "Evidence.h"
18
19#include <string>
20#include <sstream>
21#include <vector>
22#include <sys/stat.h>
23#include <regex.h>
24
25using namespace std;
26
27//
28// Class declaration
29//
30class DataHandler: public DimClient, public EvidenceServer {
31
32 struct Item {
33 DimStampedInfo *DataItem;
34 bool Exclude;
35 };
36 vector<struct Item> List;
37
38 DimCommand *LogCommand;
39 DimInfo *ServerList;
40
41 FILE *DataFile;
42 FILE *LogFile;
43 char *Filename;
44 float DataSizeMB, LogSizeMB;
45 int DataSizeLastUpdate, LogSizeLastUpdate;
46 char *BaseDir;
47 DimService *LogSizeService, *DataSizeService, *DataFilename;
48 int SizeUpdateDelay;
49 int TimeForNextFile;
50 int RollOver;
51
52 vector<regex_t> RegEx;
53
54 void infoHandler();
55 void commandHandler();
56 void AddService(string);
57 void RemoveService(string);
58 off_t FileSize(FILE *);
59
60 public:
61 DataHandler();
62 ~DataHandler();
63};
64
65//
66// Constructor
67//
68DataHandler::DataHandler(): EvidenceServer(SERVER_NAME) {
69
70 // Initialization to prevent freeing unallocated memory
71 DataFile = NULL;
72 LogFile = NULL;
73 Filename = NULL;
74
75 LogSizeService = NULL;
76 DataSizeService = NULL;
77
78 DataSizeLastUpdate = 0;
79 LogSizeLastUpdate = 0;
80 TimeForNextFile = 0;
81
82 // Request configuration data
83 BaseDir = GetConfig("basedir");
84 SizeUpdateDelay = atoi(GetConfig("sizeupdate"));
85 RollOver = atoi(GetConfig("rollover"));
86
87 // Open log file
88 if ((LogFile = fopen((string(BaseDir)+"/"+LOG_FILENAME).c_str(), "a")) == NULL) {
89 Message(FATAL, "Could not open log file (%s)", strerror(errno));
90 }
91
92 // Create services for file sizes and data file name
93 DataSizeMB = 0;
94 DataSizeService = new DimService(SERVER_NAME "/DataSizeMB", DataSizeMB);
95
96 LogSizeMB = FileSize(LogFile)/1024.0/1024.0;
97 LogSizeService = new DimService(SERVER_NAME "/LogSizeMB", LogSizeMB);
98
99 DataFilename = new DimService(SERVER_NAME "/CurrentFile", (char *) "");
100
101 // Compile regular expressions
102 char *Exclude = GetConfig("exclude");
103 char *Token = strtok(Exclude, "\t ");
104 regex_t R;
105
106 while (Token != NULL) {
107 int Ret = regcomp(&R, Token, REG_EXTENDED|REG_NOSUB);
108 if (Ret != 0) {
109 char Err[200];
110 regerror(Ret, &R, Err, sizeof(Err));
111 Message(ERROR, "Error compiling regular expression '%s' (%s)", Token, Err);
112 }
113 else RegEx.push_back(R);
114
115 Token = strtok(NULL, "\t ");
116 }
117
118 // Provide logging command
119 LogCommand = new DimCommand("DColl/Log", (char *) "C", this);
120
121 // Subsribe to top-level server list (not via AddService() due to thread issue)
122 ServerList = new DimInfo((char *) "DIS_DNS/SERVER_LIST", NO_LINK, this);
123}
124
125//
126// Destructor: Close files and free memory
127//
128DataHandler::~DataHandler() {
129
130 // Delete all DIM subscriptions
131 delete ServerList;
132 while (List.size() != 0) RemoveService(List[0].DataItem->getName());
133
134 delete LogCommand;
135 delete DataFilename;
136 delete[] Filename;
137
138 delete LogSizeService;
139 delete DataSizeService;
140
141 // Close files
142 if (LogFile != NULL) if (fclose(LogFile) != 0) {
143 Message(ERROR, "Could not close log file (%s)", strerror(errno));
144 }
145 if (DataFile != NULL && fclose(DataFile) != 0) {
146 Message(ERROR, "Error: Could not close data file (%s)", strerror(errno));
147 }
148
149 // Free memory for regular expressions handling
150 for (int i=0; i<RegEx.size(); i++) regfree(&RegEx[i]);
151}
152
153//
154// Implementation of data handling
155//
156// DIM ensures infoHandler() is called serialized, therefore
157// no mutex is needed to serialize writing to the file
158void DataHandler::infoHandler() {
159
160 DimInfo *I = getInfo();
161
162 // Check if service available
163 if (!ServiceOK(I)) return;
164
165 //
166 // ====== Part A: Handle service subscriptions ===
167 //
168 // Services are added here, removal only in constructor.
169
170 // If service is DIS_DNS/SERVER_LIST, subscribe to all SERVICE_LIST services
171 if (strcmp(I->getName(), "DIS_DNS/SERVER_LIST") == 0) {
172 char *Token = strtok(I->getString(), "+-!@");
173 while (Token != NULL) {
174 AddService(string(Token)+"/SERVICE_LIST"); // 'add' also for '-' and '!'
175 Token = strtok(NULL, "|"); // Skip server IP address
176 Token = strtok(NULL, "@");
177 }
178 return;
179 }
180
181 // If service is SERVICE_LIST of any server, scan all services.
182 // Subscribe to all services (but not to commands and RPCs)
183 if (strstr(I->getName(), "/SERVICE_LIST") != NULL) {
184 char *Name = strtok(I->getString(), "+-!|");
185 while (Name != NULL) {
186 // Check if item is a service
187 char *Type = strtok(NULL, "\n");
188 if (Type == NULL) return; // for safety, should not happen
189 if (strstr(Type, "|CMD")==NULL && strstr(Type, "|RPC")==NULL) AddService(Name); // 'add' also for '-' and '!'
190 Name = strtok(NULL, "|");
191 }
192 return;
193 }
194
195 //
196 // ====== Part B: Handle opening of data files ===
197 //
198
199 // If it is time to open new data file, close the current one
200 if (time(NULL) >= TimeForNextFile) {
201 if (DataFile != NULL && fclose(DataFile) != 0) {
202 Message(ERROR, "Error: Could not close data file (%s)", strerror(errno));
203 }
204 DataFile = NULL;
205 }
206
207 // Open new data file if necessary
208 if (DataFile == NULL) {
209 time_t Time = time(NULL);
210 struct tm *T = localtime(&Time);
211
212 // Get time structure with date rollover
213 if (T->tm_hour >= RollOver) T->tm_mday++;
214 if (mktime(T) == -1) Message(ERROR, "mktime() failed, check filename");
215
216 // Create direcory if not existing (ignore error if already existing)
217 char *Dir;
218 if (asprintf(&Dir, "%s/%d", BaseDir, T->tm_year+1900) == -1) {
219 Message(FATAL, "asprintf() failed, could not create direcory name");
220 }
221 if(mkdir(Dir, S_IRWXU|S_IRWXG)==-1 && errno!=EEXIST) {
222 Message(FATAL, "Could not create directory '%s' (%s)", Dir, strerror(errno));
223 }
224
225 // Create filename
226 free(Filename);
227 if (asprintf(&Filename, "%s/%d%02d%02d.slow", Dir, T->tm_year+1900, T->tm_mon+1, T->tm_mday) == 1) {
228 Message(FATAL, "asprintf() failed, could not create filename");
229 }
230 free(Dir);
231
232 // Open file
233 if ((DataFile = fopen(Filename, "a")) == NULL) {
234 Message(FATAL, "Could not open data file '%s' (%s)", Filename, strerror(errno));
235 }
236 else Message(INFO, "Opened data file '%s'", Filename);
237 DataFilename->updateService(Filename);
238
239 // Calculate time for next file opening
240 T->tm_sec = 0;
241 T->tm_min = 0;
242 T->tm_hour = RollOver;
243 TimeForNextFile = mktime(T);
244 }
245
246 //
247 // ====== Part C: Handle writing to data file ===
248 //
249
250 // Identify index of service
251 for (int Service=0; Service<List.size(); Service++) if (I == List[Service].DataItem) {
252
253 // Service excluded from writing?
254 if (List[Service].Exclude) return;
255
256 // Write data header
257 time_t RawTime = I->getTimestamp();
258 struct tm *TM = localtime(&RawTime);
259
260 fprintf(DataFile, "%s %d %d %d %d %d %d %d %d %lu ", I->getName(), I->getQuality(), TM->tm_year+1900, TM->tm_mon+1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec, I->getTimestampMillisecs(), I->getTimestamp());
261
262 // Translate data into ASCII
263 char *Text = EvidenceServer::ToString(I);
264
265 if (Text != NULL) {
266 // Replace new line by '\' and all other control characters by white space
267 for (int i=0; i<strlen(Text); i++) {
268 if (Text[i] == '\n') Text[i] = '\\';
269 else if (iscntrl(Text[i])) Text[i] = ' ';
270 }
271
272 // Write to file
273 fprintf(DataFile, "%s\n", Text);
274
275 free(Text);
276 }
277 else fprintf(DataFile, "Cannot interpret format identifier\n");
278
279 // Terminate if error because otherwise infinite loop might result as
280 // next call to this infoHandler() will try to (re-)open file
281 if(ferror(DataFile)) {
282 fclose(DataFile);
283 DataFile = NULL;
284 Message(FATAL, "Error writing to data file, closed file (%s)", strerror(errno));
285 }
286
287 // Update datafile size service (not every time to avoid infinite loop)
288 if (time(NULL) - DataSizeLastUpdate > SizeUpdateDelay) {
289 fflush(DataFile); // not continuously to reduce load
290
291 DataSizeMB = FileSize(DataFile)/1024.0/1024.0;
292 DataSizeService->updateService();
293 DataSizeLastUpdate = time(NULL);
294 }
295 } // Check for disk writing
296}
297
298//
299// Implementation of log writing
300//
301void DataHandler::commandHandler() {
302
303 if (getCommand() != LogCommand || LogFile == NULL) return;
304
305 // Replace all carriage-return by line feed and non-printable characters
306 char *Text = getCommand()->getString();
307 for (unsigned int i=0; i<strlen(Text); i++) {
308 if (Text[i] == '\r') Text[i] = '\n';
309 if(isprint(Text[i])==0 && isspace(Text[i])==0) Text[i] = ' ';
310 }
311
312 time_t RawTime = time(NULL);
313 struct tm *TM = localtime(&RawTime);
314
315 fprintf(LogFile, "%2d/%2d/%4d %2d:%2d:%2d %s (ID %d): %s\n",
316 TM->tm_mday, TM->tm_mon+1, TM->tm_year+1900,
317 TM->tm_hour, TM->tm_min, TM->tm_sec, getClientName(), getClientId(), Text);
318
319 fflush(LogFile);
320
321 // If error close file (otherwise infinite loop because Message() also writes to log)
322 if(ferror(LogFile)) {
323 fclose(LogFile);
324 LogFile = NULL;
325 Message(ERROR, "Error writing to log file, closing file (%s)", strerror(errno));
326 }
327
328 // Update logfile size service
329 if (time(NULL) - LogSizeLastUpdate > SizeUpdateDelay) {
330 LogSizeMB = FileSize(LogFile)/1024.0/1024.0;
331 LogSizeService->updateService();
332 LogSizeLastUpdate = time(NULL);
333 }
334}
335
336
337//
338// Add service to watch list
339//
340void DataHandler::AddService(string Name) {
341
342 struct Item New;
343
344 // Check if already subscribed to this service
345 for (int i=0; i<List.size(); i++) {
346 if (Name == List[i].DataItem->getName()) return;
347 }
348
349 // Should service be ignored?
350 New.Exclude = false;
351 for (int i=0; i<RegEx.size(); i++) {
352 if (regexec(&RegEx[i], Name.c_str(), (size_t) 0, NULL, 0) == 0) New.Exclude = true;
353 }
354
355 // Subscribe to service
356 New.DataItem = new DimStampedInfo(Name.c_str(), NO_LINK, this);
357
358 List.push_back(New);
359}
360
361
362//
363// Remove service from watch list
364//
365void DataHandler::RemoveService(string Name) {
366
367 // Find service index
368 vector<struct Item>::iterator E;
369 for (E=List.begin(); E<List.end(); ++E) if (Name == (*E).DataItem->getName()) {
370 delete (*E).DataItem;
371 List.erase(E);
372 }
373}
374
375//
376// Determine size of file in kB
377//
378off_t DataHandler::FileSize(FILE *File) {
379
380 struct stat FileStatus;
381
382 if (fstat(fileno(File), &FileStatus) == -1) {
383 Message(WARN, "Could not determine size of file (%s)", strerror(errno));
384 return -1;
385 }
386 return FileStatus.st_size;
387}
388
389
390//
391// Main program
392//
393int main() {
394
395 // Static ensures calling of destructor by exit()
396 static DataHandler DataInstance;
397
398 // Sleep until signal caught
399 pause();
400}
Note: See TracBrowser for help on using the repository browser.