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 | - For each service, it creates a new service with '.hist' appended that |
---|
8 | contains a history of events kept within a ring buffer. Each entry will |
---|
9 | be the result of a conversion to double of the text written to the data file. |
---|
10 | - The command 'DColl/Log' writes the associated string to the log |
---|
11 | file specified in the configuration. |
---|
12 | |
---|
13 | Oliver Grimm, December 2009 |
---|
14 | |
---|
15 | \********************************************************************/ |
---|
16 | |
---|
17 | #define SERVER_NAME "DColl" |
---|
18 | |
---|
19 | #include "../Evidence.h" |
---|
20 | |
---|
21 | #define NO_LINK "__&DIM&NOLINK&__" // for checking if DIMserver is alive |
---|
22 | |
---|
23 | // |
---|
24 | // Class declaration |
---|
25 | // |
---|
26 | class DataHandler: public DimClient, public DimCommand, |
---|
27 | public DimBrowser, public EvidenceServer { |
---|
28 | private: |
---|
29 | pthread_mutex_t DataMutex; |
---|
30 | pthread_mutex_t LogMutex; |
---|
31 | |
---|
32 | unsigned int NumItems; |
---|
33 | FILE *DataFile; |
---|
34 | FILE *LogFile; |
---|
35 | char *Filename; |
---|
36 | |
---|
37 | DimStampedInfo **DataItem; |
---|
38 | DimService **HistService; |
---|
39 | int HistSize; |
---|
40 | struct EvidenceHistoryItem **HistBuffer; |
---|
41 | int *HistPointer; |
---|
42 | |
---|
43 | void infoHandler(); |
---|
44 | void commandHandler(); |
---|
45 | bool ReallocMem(void *&, int); |
---|
46 | |
---|
47 | public: |
---|
48 | DataHandler(); |
---|
49 | ~DataHandler(); |
---|
50 | }; |
---|
51 | |
---|
52 | // |
---|
53 | // Constructor |
---|
54 | // |
---|
55 | DataHandler::DataHandler(): DimCommand((char *) "DColl/Log", (char *) "C"), EvidenceServer(SERVER_NAME) { |
---|
56 | |
---|
57 | // Initialization to prevent freeing unallocated memory |
---|
58 | DataFile = NULL; |
---|
59 | Filename = NULL; |
---|
60 | |
---|
61 | DataItem = NULL; |
---|
62 | HistService = NULL; |
---|
63 | HistBuffer = NULL; |
---|
64 | HistPointer = NULL; |
---|
65 | |
---|
66 | // Request configuration data |
---|
67 | char *Items = GetConfig(SERVER_NAME " items"); |
---|
68 | char *DataDir = GetConfig(SERVER_NAME " datadir"); |
---|
69 | char *Logname = GetConfig(SERVER_NAME " logfile"); |
---|
70 | HistSize = atoi(GetConfig(SERVER_NAME " histsize")); |
---|
71 | if (HistSize < 1) HistSize = 1; // Minimum one items |
---|
72 | |
---|
73 | // Create mutex for thread synchronization |
---|
74 | if ((errno=pthread_mutex_init(&DataMutex, NULL)) != 0) { |
---|
75 | Msg(FATAL, "pthread_mutex_init() failed for data file (%s)", strerror(errno)); |
---|
76 | } |
---|
77 | if ((errno=pthread_mutex_init(&LogMutex, NULL)) != 0) { |
---|
78 | Msg(FATAL, "pthread_mutex_init() failed for log file (%s)", strerror(errno)); |
---|
79 | } |
---|
80 | |
---|
81 | // Interpret DIM services to observe and prepare history buffer |
---|
82 | char *Buf, *ServiceName, *Format; |
---|
83 | int NumServices; |
---|
84 | NumItems = 0; |
---|
85 | char *NextToken = strtok(Items, " \t"); |
---|
86 | while (NextToken != NULL) { |
---|
87 | NumServices = getServices(NextToken); |
---|
88 | for (int j=0; j<NumServices; j++) { |
---|
89 | if (getNextService(ServiceName, Format) == DimSERVICE) { |
---|
90 | |
---|
91 | // Check if already subsubed to this service |
---|
92 | for (int i=0; i<NumItems; i++) { |
---|
93 | if(strcmp(ServiceName, DataItem[i]->getName()) == 0) continue; |
---|
94 | } |
---|
95 | |
---|
96 | // Increase capacity of arrays |
---|
97 | if (!ReallocMem((void *&) DataItem, NumItems+1)) continue; |
---|
98 | if (!ReallocMem((void *&) HistService, NumItems+1)) continue; |
---|
99 | if (!ReallocMem((void *&) HistBuffer, NumItems+1)) continue; |
---|
100 | if (!ReallocMem((void *&) HistPointer, NumItems+1)) continue; |
---|
101 | |
---|
102 | // Subscribe to service |
---|
103 | DataItem[NumItems] = new DimStampedInfo(ServiceName, (char *) NO_LINK, this); |
---|
104 | |
---|
105 | // Create history service |
---|
106 | HistBuffer[NumItems] = new struct EvidenceHistoryItem [HistSize]; |
---|
107 | memset(HistBuffer[NumItems], 0, HistSize*sizeof(EvidenceHistoryItem)); |
---|
108 | HistPointer[NumItems] = 0; |
---|
109 | |
---|
110 | if (asprintf(&Buf, "%s.hist", ServiceName) == -1) { |
---|
111 | Msg(ERROR, "Could not create Hist service for %s because asprintf() failed", ServiceName); |
---|
112 | } |
---|
113 | else { |
---|
114 | HistService[NumItems] = new DimService (Buf, (char *) "C", |
---|
115 | HistBuffer[NumItems], HistSize*sizeof(EvidenceHistoryItem)); |
---|
116 | free(Buf); |
---|
117 | } |
---|
118 | NumItems++; |
---|
119 | } |
---|
120 | } |
---|
121 | NextToken = strtok(NULL, " \t"); |
---|
122 | } |
---|
123 | |
---|
124 | // Open data file |
---|
125 | time_t rawtime = time(NULL); |
---|
126 | struct tm * timeinfo = gmtime(&rawtime); |
---|
127 | if(timeinfo->tm_hour >= 13) rawtime += 12*60*60; |
---|
128 | timeinfo = gmtime(&rawtime); |
---|
129 | |
---|
130 | if (asprintf(&Filename, "%s/%d%02d%02d.slow", DataDir, timeinfo->tm_year+1900, timeinfo->tm_mon+1, timeinfo->tm_mday) == -1) { |
---|
131 | Filename = NULL; |
---|
132 | Msg(FATAL, "Could not create filename with asprintf()(%s)", strerror(errno)); |
---|
133 | } |
---|
134 | if ((DataFile = fopen(Filename, "a")) == NULL) { |
---|
135 | Msg(FATAL, "Could not open data file '%s' (%s)", Filename, strerror(errno)); |
---|
136 | } |
---|
137 | |
---|
138 | // Open log file |
---|
139 | if ((LogFile = fopen(Logname, "a")) == NULL) { |
---|
140 | Msg(FATAL, "Could not open log file '%s' (%s)", Logname, strerror(errno)); |
---|
141 | } |
---|
142 | |
---|
143 | } |
---|
144 | |
---|
145 | // |
---|
146 | // Destructor: Close files and free memory |
---|
147 | // |
---|
148 | DataHandler::~DataHandler() { |
---|
149 | |
---|
150 | // Close files |
---|
151 | if (LogFile != NULL && fclose(LogFile) != 0) { |
---|
152 | Msg(ERROR, "Could not close log file (%s)", strerror(errno)); |
---|
153 | } |
---|
154 | if (DataFile != NULL && fclose(DataFile) != 0) { |
---|
155 | Msg(ERROR, "Error: Could not close data file (%s)", strerror(errno)); |
---|
156 | } |
---|
157 | |
---|
158 | // Free all memory |
---|
159 | for (int i=0; i<NumItems; i++) { |
---|
160 | delete[] HistBuffer[i]; |
---|
161 | delete DataItem[i]; |
---|
162 | } |
---|
163 | |
---|
164 | free(Filename); |
---|
165 | free(HistService); |
---|
166 | free(HistPointer); |
---|
167 | free(HistBuffer); |
---|
168 | free(DataItem); |
---|
169 | |
---|
170 | // Destroy mutex |
---|
171 | pthread_mutex_destroy (&LogMutex); |
---|
172 | pthread_mutex_destroy (&DataMutex); |
---|
173 | } |
---|
174 | |
---|
175 | // |
---|
176 | // Implementation of data handling |
---|
177 | // |
---|
178 | // More than one infoHandler() might run in parallel, therefore |
---|
179 | // the mutex mechanism is used to serialize writing to the file |
---|
180 | void DataHandler::infoHandler() { |
---|
181 | |
---|
182 | DimInfo *Info = getInfo(); |
---|
183 | |
---|
184 | // Check if service actually available, if it contains data and if data file is open |
---|
185 | if (Info->getSize()==strlen(NO_LINK)+1 && strcmp(Info->getString(), NO_LINK)==0) return; |
---|
186 | if (Info->getSize() == 0 || DataFile == NULL) return; |
---|
187 | |
---|
188 | // Identify index of service |
---|
189 | int Service; |
---|
190 | for (Service=0; Service<NumItems; Service++) if (Info == DataItem[Service]) break; |
---|
191 | if (Service == NumItems) return; // Not found: should never happen |
---|
192 | |
---|
193 | pthread_mutex_lock(&DataMutex); |
---|
194 | |
---|
195 | // Write data header |
---|
196 | time_t RawTime = Info->getTimestamp(); |
---|
197 | struct tm *TM = localtime(&RawTime); |
---|
198 | |
---|
199 | fprintf(DataFile, "%s %d %d %d %d %d %d %d %lu ", Info->getName(), TM->tm_year+1900, TM->tm_mon+1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec, Info->getTimestampMillisecs(), Info->getTimestamp()); |
---|
200 | |
---|
201 | // Translate info data into ASCII and write to file and to history buffer |
---|
202 | char *Text = EvidenceServer::ToString(Info); |
---|
203 | if (Text != NULL) { |
---|
204 | fprintf(DataFile, "%s\n", Text); |
---|
205 | |
---|
206 | HistBuffer[Service][HistPointer[Service]].Seconds = Info->getTimestamp(); |
---|
207 | HistBuffer[Service][HistPointer[Service]].Value = atof(Text); |
---|
208 | HistService[Service]->updateService(); |
---|
209 | HistPointer[Service]++; |
---|
210 | if (HistPointer[Service] >= HistSize) HistPointer[Service] = 0; |
---|
211 | |
---|
212 | free(Text); |
---|
213 | } |
---|
214 | else fprintf(DataFile, "Cannot interpret format identifier\n"); |
---|
215 | |
---|
216 | fflush(DataFile); |
---|
217 | if(ferror(DataFile)) Msg(ERROR, "Error writing to data file (%s)", strerror(errno)); |
---|
218 | |
---|
219 | pthread_mutex_unlock(&DataMutex); |
---|
220 | } |
---|
221 | |
---|
222 | // |
---|
223 | // Implementation of log writing (the only command is 'DColl/Log') |
---|
224 | // |
---|
225 | void DataHandler::commandHandler() { |
---|
226 | |
---|
227 | if (LogFile == NULL) return; // Handler might be called before file is opened |
---|
228 | |
---|
229 | pthread_mutex_lock(&LogMutex); |
---|
230 | |
---|
231 | time_t RawTime = time(NULL); |
---|
232 | struct tm *TM = localtime(&RawTime); |
---|
233 | |
---|
234 | fprintf(LogFile, "%d/%d/%d %d:%d:%d: %s\n", |
---|
235 | TM->tm_mday, TM->tm_mon+1, TM->tm_year+1900, |
---|
236 | TM->tm_hour, TM->tm_min, TM->tm_sec, getString()); |
---|
237 | |
---|
238 | fflush(LogFile); |
---|
239 | if(ferror(LogFile)) Msg(ERROR, "Error writing to log file (%s)", strerror(errno)); |
---|
240 | pthread_mutex_unlock(&LogMutex); |
---|
241 | } |
---|
242 | |
---|
243 | |
---|
244 | // |
---|
245 | // Implementation of memory re-allocation for pointer arrays |
---|
246 | // |
---|
247 | bool DataHandler::ReallocMem(void *&Memory, int Size) { |
---|
248 | |
---|
249 | void *NewMem = realloc(Memory, Size*sizeof(void *)); |
---|
250 | |
---|
251 | if (NewMem != NULL) { |
---|
252 | Memory = NewMem; |
---|
253 | return true; |
---|
254 | } |
---|
255 | else { |
---|
256 | Msg(ERROR, "Could not allocate memory ()", strerror(errno)); |
---|
257 | return false; |
---|
258 | } |
---|
259 | } |
---|
260 | |
---|
261 | // |
---|
262 | // Main program |
---|
263 | // |
---|
264 | int main() { |
---|
265 | |
---|
266 | // Static ensures calling of destructor by exit() |
---|
267 | static DataHandler Data; |
---|
268 | |
---|
269 | pause(); // Sleep until signal caught |
---|
270 | } |
---|