Line data Source code
1 : /********************************************************************\
2 :
3 : Name: mlogger.cxx
4 : Created by: Stefan Ritt
5 :
6 : Contents: MIDAS logger program
7 :
8 : $Id$
9 :
10 : \********************************************************************/
11 :
12 : #undef NDEBUG // midas required assert() to be always enabled
13 :
14 : #include "midas.h"
15 : #include "msystem.h"
16 : #include "hardware.h"
17 : #include "mstrlcpy.h"
18 : #include <errno.h> /* for mkdir() */
19 : #include <assert.h>
20 : #include <string>
21 :
22 : #define HAVE_LOGGING
23 : #include "mdsupport.h"
24 :
25 : #ifdef HAVE_ROOT
26 : #undef GetCurrentTime
27 : #include "TApplication.h"
28 : #include "TFile.h"
29 : #include "TNtuple.h"
30 : #include "TLeaf.h"
31 : //#warning HAVE_ROOT!
32 : //#else
33 : //#warning NO HAVE_ROOT!
34 : #endif
35 :
36 : #ifdef OS_WINNT
37 : #define ZLIB_WINAPI
38 : #endif
39 : #include <zlib.h>
40 :
41 : #ifdef HAVE_MYSQL
42 : #ifdef OS_UNIX
43 : #include <mysql.h>
44 : #include <mysqld_error.h>
45 : #endif
46 : #ifdef OS_WINNT
47 : #include <mysql.h>
48 : #include <mysqld_error.h>
49 : int errno; // under NT, "ignore libcd" is required, so errno has to be defined here
50 : #endif
51 : void create_runlog_sql_tree();
52 : #endif
53 :
54 : void create_runlog_ascii_tree();
55 : void create_runlog_json_tree();
56 :
57 0 : static std::string IntToString(int value)
58 : {
59 : char buf[256];
60 0 : sprintf(buf, "%d", value);
61 0 : return buf;
62 : }
63 :
64 0 : static std::string TimeToString(time_t t)
65 : {
66 0 : const char* sign = "";
67 :
68 0 : if (t == 0)
69 0 : return "0";
70 :
71 0 : time_t tt = t;
72 :
73 0 : if (t < 0) {
74 0 : sign = "-";
75 0 : tt = -t;
76 : }
77 :
78 0 : assert(tt > 0);
79 :
80 0 : std::string v;
81 0 : while (tt) {
82 0 : char c = '0' + tt%10;
83 0 : tt /= 10;
84 0 : v = c + v;
85 : }
86 :
87 0 : v = sign + v;
88 :
89 : //printf("time %.0f -> %s\n", (double)t, v.c_str());
90 :
91 0 : return v;
92 0 : }
93 :
94 : /*---- Logging channel information ---------------------------------*/
95 :
96 : // NOTE: [Settings] here MUST be exactly same as CHN_SETTINGS_STR below
97 :
98 : #define CHN_TREE_STR(_name) const char *_name[] = {\
99 : "[Settings]",\
100 : "Active = BOOL : 1",\
101 : "Type = STRING : [8] Disk",\
102 : "Filename = STRING : [256] run%05d.mid",\
103 : "Format = STRING : [8] MIDAS",\
104 : "Compression = INT32 : 0",\
105 : "ODB dump = BOOL : 1",\
106 : "ODB dump format = STRING : [32] json",\
107 : "Options ODB dump format = STRING[3] :",\
108 : "[32] odb",\
109 : "[32] xml",\
110 : "[32] json",\
111 : "Log messages = UINT32 : 0",\
112 : "Buffer = STRING : [32] SYSTEM",\
113 : "Event ID = INT32 : -1",\
114 : "Trigger mask = INT32 : -1",\
115 : "Event limit = DOUBLE : 0",\
116 : "Byte limit = DOUBLE : 0",\
117 : "Subrun Byte limit = DOUBLE : 0",\
118 : "Tape capacity = DOUBLE : 0",\
119 : "Subdir format = STRING : [32]",\
120 : "Current filename = STRING : [256]",\
121 : "Data checksum = STRING : [256] CRC32C",\
122 : "Options Data checksum = STRING[5] :",\
123 : "[32] NONE",\
124 : "[32] CRC32C",\
125 : "[32] SHA256",\
126 : "[32] SHA512",\
127 : "[32] ZLIB",\
128 : "File checksum = STRING : [256] CRC32C",\
129 : "Options File checksum = STRING[5] :",\
130 : "[32] NONE",\
131 : "[32] CRC32C",\
132 : "[32] SHA256",\
133 : "[32] SHA512",\
134 : "[32] ZLIB",\
135 : "Compress = STRING : [256] lz4",\
136 : "Options Compress = STRING[5] :",\
137 : "[32] none",\
138 : "[32] gzip",\
139 : "[32] lz4",\
140 : "[32] bzip2",\
141 : "[32] pbzip2",\
142 : "Output = STRING : [256] FILE",\
143 : "Options Output = STRING[5] :",\
144 : "[32] NULL",\
145 : "[32] FILE",\
146 : "[32] FTP",\
147 : "[32] ROOT",\
148 : "[32] PIPE",\
149 : "Gzip compression = UINT32 : 0",\
150 : "Bzip2 compression = UINT32 : 0",\
151 : "Pbzip2 num cpu = UINT32 : 0",\
152 : "Pbzip2 compression = UINT32 : 0",\
153 : "Pbzip2 options = STRING : [256]",\
154 : "",\
155 : "[Statistics]",\
156 : "Events written = DOUBLE : 0",\
157 : "Bytes written = DOUBLE : 0",\
158 : "Bytes written uncompressed = DOUBLE : 0",\
159 : "Bytes written total = DOUBLE : 0",\
160 : "Bytes written subrun = DOUBLE : 0",\
161 : "Files written = DOUBLE : 0",\
162 : "Disk level = DOUBLE : 0",\
163 : "",\
164 : NULL}
165 :
166 : typedef struct {
167 : BOOL active;
168 : char obsolete_type[8];
169 : char filename[256];
170 : char obsolete_format[8];
171 : INT obsolete_compression;
172 : BOOL odb_dump;
173 : char odb_dump_format[32];
174 : char options_odb_dump_format[3][32];
175 : DWORD log_messages;
176 : char buffer[32];
177 : INT event_id;
178 : INT trigger_mask;
179 : double event_limit;
180 : double byte_limit;
181 : double subrun_byte_limit;
182 : double obsolete_tape_capacity;
183 : char subdir_format[32];
184 : char current_filename[256];
185 : char data_checksum[256];
186 : char options_data_checksum[5][32];
187 : char file_checksum[256];
188 : char options_file_checksum[5][32];
189 : char compress[256];
190 : char options_compress[5][32];
191 : char output[256];
192 : char options_output[5][32];
193 : uint32_t gzip_compression;
194 : uint32_t bzip2_compression;
195 : uint32_t pbzip2_num_cpu;
196 : uint32_t pbzip2_compression;
197 : char pbzip2_options[256];
198 : } CHN_SETTINGS;
199 :
200 : // NOTE: CHN_SETTINGS here MUST be exactly same as [Settings] in CHN_TREE_STR above.
201 :
202 : #define CHN_SETTINGS_STR(_name) const char *_name[] = { \
203 : "Active = BOOL : 1",\
204 : "Type = STRING : [8] Disk",\
205 : "Filename = STRING : [256] run%05d.mid",\
206 : "Format = STRING : [8] MIDAS",\
207 : "Compression = INT32 : 0",\
208 : "ODB dump = BOOL : 1",\
209 : "ODB dump format = STRING : [32] json",\
210 : "Log messages = UINT32 : 0",\
211 : "Buffer = STRING : [32] SYSTEM",\
212 : "Event ID = INT32 : -1",\
213 : "Trigger mask = INT32 : -1",\
214 : "Event limit = DOUBLE : 0",\
215 : "Byte limit = DOUBLE : 0",\
216 : "Subrun Byte limit = DOUBLE : 0",\
217 : "Tape capacity = DOUBLE : 0",\
218 : "Subdir format = STRING : [32]",\
219 : "Current filename = STRING : [256]",\
220 : "Data checksum = STRING : [256]",\
221 : "File checksum = STRING : [256]",\
222 : "Compress = STRING : [256]",\
223 : "Output = STRING : [256]",\
224 : "Gzip compression = UINT32 : 0",\
225 : "Bzip2 compression = UINT32 : 0",\
226 : "Pbzip2 num cpu = UINT32 : 0",\
227 : "Pbzip2 compression = UINT32 : 0",\
228 : "Pbzip2 options = STRING : [256]",\
229 : "",\
230 : NULL}
231 :
232 : struct CHN_STATISTICS {
233 : double events_written = 0; /* count events, reset in tr_start() */
234 : double bytes_written = 0; /* count bytes written out (compressed), reset in tr_start() */
235 : double bytes_written_uncompressed = 0; /* count bytes before compression, reset in tr_start() */
236 : double bytes_written_total = 0; /* count bytes written out (compressed), reset in log_callback(RPC_LOG_REWIND) */
237 : double bytes_written_subrun = 0; /* count bytes written out (compressed), reset in tr_start() and on subrun increment */
238 : double files_written = 0; /* incremented in log_close(), reset in log_callback(RPC_LOG_REWIND) */
239 : double disk_level = 0;
240 : };
241 :
242 : #define CHN_STATISTICS_STR(_name) const char *_name[] = {\
243 : "Events written = DOUBLE : 0",\
244 : "Bytes written = DOUBLE : 0",\
245 : "Bytes written uncompressed = DOUBLE : 0",\
246 : "Bytes written total = DOUBLE : 0",\
247 : "Bytes written subrun = DOUBLE : 0",\
248 : "Files written = DOUBLE : 0",\
249 : "Disk level = DOUBLE : 0",\
250 : "",\
251 : NULL}
252 :
253 : /*---- logger channel definition---------------------------------------*/
254 :
255 : class WriterInterface;
256 : struct MIDAS_INFO;
257 : #ifdef HAVE_ROOT
258 : struct TREE_STRUCT;
259 : #endif
260 :
261 : struct LOG_CHN {
262 : std::string name;
263 : INT handle = 0;
264 : std::string path;
265 : std::string pipe_command;
266 : INT type = LOG_TYPE_DISK;
267 : #ifdef OBSOLETE
268 : INT format = 0;
269 : INT compression = 0;
270 : #endif
271 : INT subrun_number = 0;
272 : INT buffer_handle = 0;
273 : INT msg_buffer_handle = 0;
274 : INT request_id = 0;
275 : INT msg_request_id = 0;
276 : HNDLE stats_hkey = 0;
277 : HNDLE settings_hkey = 0;
278 : CHN_SETTINGS settings;
279 : CHN_STATISTICS statistics;
280 : MIDAS_INFO *midas_info = NULL;
281 : #ifdef HAVE_ROOT
282 : TREE_STRUCT *root_tree_struct = NULL;
283 : #endif
284 : #ifdef OBSOLETE
285 : FTP_CON *ftp_con = NULL;
286 : void *gzfile = NULL;
287 : FILE *pfile = NULL;
288 : #endif
289 : void *ftp_con = NULL;
290 : void *pfile = NULL;
291 : WriterInterface *writer = NULL;
292 : DWORD last_checked = 0;
293 : BOOL do_disk_level = 0;
294 : int pre_checksum_module = 0; // CHECKSUM_xxx
295 : int compression_module = 0; // COMPRESS_xxx
296 : int post_checksum_module = 0; // CHECKSUM_xxx
297 : int output_module = 0; // OUTPUT_xxx
298 : };
299 :
300 0 : LOG_CHN* new_LOG_CHN(const char* name)
301 : {
302 0 : LOG_CHN* chn = new LOG_CHN;
303 0 : chn->name = name;
304 : // chn->settings clear settings
305 0 : return chn;
306 : };
307 :
308 : /*---- globals -----------------------------------------------------*/
309 :
310 : #define DISK_CHECK_INTERVAL_MILLISEC 10000
311 :
312 : INT local_state;
313 : BOOL in_stop_transition = FALSE;
314 : BOOL tape_message = TRUE;
315 : BOOL verbose = FALSE;
316 : BOOL stop_requested = FALSE;
317 : BOOL start_requested = FALSE;
318 : DWORD auto_restart = 0;
319 : DWORD run_start_time, subrun_start_time;
320 : double stop_try_later = 0;
321 :
322 : std::vector<LOG_CHN*> log_channels;
323 :
324 : struct hist_log_s {
325 : char event_name[256];
326 : char *buffer;
327 : INT buffer_size;
328 : HNDLE hKeyVar;
329 : DWORD n_var;
330 : time_t min_period;
331 : time_t last_log;
332 : };
333 :
334 : static int hist_log_size = 0;
335 : static int hist_log_max = 0;
336 : static struct hist_log_s *hist_log = NULL;
337 :
338 : static HNDLE hDB;
339 :
340 : /*---- ODB records -------------------------------------------------*/
341 :
342 : static CHN_SETTINGS_STR(chn_settings_str);
343 : static CHN_STATISTICS_STR(chn_statistics_str);
344 : static CHN_TREE_STR(chn_tree_str);
345 :
346 : /*---- data structures for MIDAS format ----------------------------*/
347 :
348 : struct MIDAS_INFO {
349 : char *buffer;
350 : char *write_pointer;
351 : };
352 :
353 : /*---- forward declarations ----------------------------------------*/
354 :
355 : void receive_event(HNDLE hBuf, HNDLE request_id, EVENT_HEADER * pheader, void *pevent);
356 : INT log_write(LOG_CHN * log_chn, EVENT_HEADER * pheader);
357 : void log_system_history(HNDLE hDB, HNDLE hKey, void *info);
358 : int log_generate_file_name(LOG_CHN *log_chn);
359 : extern void start_image_history();
360 : extern void stop_image_history();
361 : extern int get_number_image_history_threads();
362 :
363 : /*== common code FAL/MLOGGER start =================================*/
364 :
365 : #define MEMZERO(obj) memset(&(obj), 0, sizeof(obj))
366 :
367 : #define FREE(ptr) if (ptr) free(ptr); (ptr)=NULL;
368 : #define DELETE(ptr) if (ptr) delete (ptr); (ptr)=NULL;
369 :
370 : /*---- writer helper --------------------------------------------*/
371 :
372 0 : static std::string xpathname(const char* xpath, int level)
373 : {
374 0 : std::string path = xpath;
375 0 : while (level > 0) {
376 0 : size_t p = path.rfind(".");
377 : //printf("level %d, path [%s], pos %d\n", level, path.c_str(), (int)p);
378 0 : if (p == std::string::npos)
379 0 : break;
380 0 : path = path.substr(0, p);
381 0 : level--;
382 : }
383 0 : return path;
384 0 : }
385 :
386 0 : static FILE* fopen_wx(const char* filename)
387 : {
388 0 : int fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IRGRP|S_IROTH);
389 0 : if (fd == -1)
390 0 : return NULL;
391 0 : return fdopen(fd, "w");
392 : }
393 :
394 0 : static bool check_file_exists(const char* filename)
395 : {
396 : struct stat st;
397 0 : int status = stat(filename, &st);
398 0 : if (status == -1) {
399 0 : return false;
400 : }
401 0 : return true;
402 : }
403 :
404 : /*---- writer interface --------------------------------------------*/
405 :
406 : class WriterInterface
407 : {
408 : public:
409 : virtual int wr_open(LOG_CHN* log_chn, int run_number) = 0;
410 : virtual int wr_write(LOG_CHN* log_chn, const void* data, const int size) = 0;
411 : virtual int wr_close(LOG_CHN* log_chn, int run_number) = 0;
412 : WriterInterface(); // base ctor
413 0 : virtual ~WriterInterface() {}; // dtor
414 0 : virtual std::string wr_get_file_ext() { return ""; }
415 : virtual std::string wr_get_chain() = 0;
416 : public:
417 : bool fTrace; // enable tracing printout
418 : double fBytesIn; // count bytes in (before compression)
419 : double fBytesOut; // count bytes out (after compression)
420 : };
421 :
422 0 : WriterInterface::WriterInterface()
423 : {
424 : //fTrace = true; // <------ to enable (disable) tracing printout, set to "true" ("false")
425 0 : fTrace = false; // <------ to enable (disable) tracing printout, set to "true" ("false")
426 0 : fBytesIn = 0;
427 0 : fBytesOut = 0;
428 :
429 0 : if (fTrace)
430 0 : printf("WriterInterface: default constructor!\n");
431 0 : }
432 :
433 : /*---- Null writer ------------------------------------------------*/
434 :
435 : class WriterNull : public WriterInterface
436 : {
437 : public:
438 0 : WriterNull(LOG_CHN* log_chn) // ctor
439 0 : {
440 0 : if (fTrace)
441 0 : printf("WriterNull: path [%s]\n", log_chn->path.c_str());
442 0 : fSimulateCompression = false;
443 0 : }
444 :
445 0 : ~WriterNull() // dtor
446 0 : {
447 0 : if (fTrace)
448 0 : printf("WriterNull: destructor\n");
449 0 : }
450 :
451 0 : int wr_open(LOG_CHN* log_chn, int run_number)
452 : {
453 0 : if (fTrace)
454 0 : printf("WriterNull: open path [%s]\n", log_chn->path.c_str());
455 0 : fBytesIn = 0;
456 0 : fBytesOut = 0;
457 0 : log_chn->handle = 9999;
458 0 : if (fSimulateCompression)
459 0 : fBytesOut += 10; // simulate compression header
460 0 : return SUCCESS;
461 : }
462 :
463 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
464 : {
465 0 : if (fTrace)
466 0 : printf("WriterNull: write path [%s], size %d\n", log_chn->path.c_str(), size);
467 0 : fBytesIn += size;
468 0 : if (fSimulateCompression)
469 0 : fBytesOut += size/3; // simulate compression
470 : else
471 0 : fBytesOut += size;
472 0 : return SUCCESS;
473 : }
474 :
475 0 : int wr_close(LOG_CHN* log_chn, int run_number)
476 : {
477 0 : if (fTrace)
478 0 : printf("WriterNull: close path [%s]\n", log_chn->path.c_str());
479 0 : if (fSimulateCompression)
480 0 : fBytesOut += 20; // simulate compression footer
481 0 : log_chn->handle = 0;
482 0 : return SUCCESS;
483 : }
484 :
485 0 : std::string wr_get_file_ext()
486 : {
487 0 : return ".null";
488 : }
489 :
490 0 : std::string wr_get_chain()
491 : {
492 0 : return "NULL";
493 : }
494 :
495 : private:
496 : bool fSimulateCompression;
497 : };
498 :
499 : /*---- file writer -------------------------------------------------*/
500 :
501 : class WriterFile : public WriterInterface
502 : {
503 : public:
504 0 : WriterFile(LOG_CHN* log_chn) // ctor
505 0 : {
506 0 : if (fTrace)
507 0 : printf("WriterFile: path [%s]\n", log_chn->path.c_str());
508 0 : fFileno = -1;
509 0 : }
510 :
511 0 : ~WriterFile() // dtor
512 0 : {
513 0 : if (fTrace)
514 0 : printf("WriterFile: destructor\n");
515 0 : fFileno = -1;
516 0 : }
517 :
518 0 : int wr_open(LOG_CHN* log_chn, int run_number)
519 : {
520 0 : fBytesIn = 0;
521 0 : fBytesOut = 0;
522 :
523 0 : if (fTrace)
524 0 : printf("WriterFile: open path [%s]\n", log_chn->path.c_str());
525 :
526 0 : assert(fFileno < 0);
527 :
528 0 : if (check_file_exists(log_chn->path.c_str()))
529 0 : return SS_FILE_EXISTS;
530 :
531 : #ifdef OS_WINNT
532 : fFileno = (int) CreateFile(log_chn->path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_SEQUENTIAL_SCAN, 0);
533 : #else
534 0 : fFileno = open(log_chn->path.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_BINARY | O_LARGEFILE, 0444);
535 : #endif
536 0 : if (fFileno < 0) {
537 0 : cm_msg(MERROR, "WriterFile::wr_open", "Cannot write to file \'%s\', open() errno %d (%s)", log_chn->path.c_str(), errno, strerror(errno));
538 0 : return SS_FILE_ERROR;
539 : }
540 :
541 0 : log_chn->handle = fFileno;
542 :
543 0 : fFilename = log_chn->path;
544 0 : return SUCCESS;
545 : }
546 :
547 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
548 : {
549 0 : if (fTrace)
550 0 : printf("WriterFile: write path [%s], size %d\n", log_chn->path.c_str(), size);
551 :
552 0 : if (size == 0)
553 0 : return SUCCESS;
554 :
555 0 : assert(fFileno >= 0);
556 :
557 0 : fBytesIn += size;
558 :
559 0 : int wr = write(fFileno, data, size);
560 :
561 0 : if (wr > 0)
562 0 : fBytesOut += wr;
563 :
564 0 : if (wr != size) {
565 0 : cm_msg(MERROR, "WriterFile::wr_write", "Cannot write to file \'%s\', write(%d) returned %d, errno: %d (%s)", log_chn->path.c_str(), size, wr, errno, strerror(errno));
566 0 : return SS_FILE_ERROR;
567 : }
568 :
569 0 : return SUCCESS;
570 : }
571 :
572 0 : int wr_close(LOG_CHN* log_chn, int run_number)
573 : {
574 : int err;
575 :
576 0 : if (fTrace)
577 0 : printf("WriterFile: close path [%s]\n", log_chn->path.c_str());
578 :
579 0 : assert(fFileno >= 0);
580 :
581 0 : log_chn->handle = 0;
582 :
583 0 : err = close(fFileno);
584 0 : fFileno = -1;
585 :
586 0 : if (err != 0) {
587 0 : cm_msg(MERROR, "WriterFile::wr_close", "Cannot write to file \'%s\', close() errno %d (%s)", log_chn->path.c_str(), errno, strerror(errno));
588 0 : return SS_FILE_ERROR;
589 : }
590 :
591 0 : return SUCCESS;
592 : }
593 :
594 0 : std::string wr_get_chain()
595 : {
596 0 : return ">" + fFilename;
597 : }
598 :
599 : private:
600 : std::string fFilename;
601 : int fFileno;
602 : };
603 :
604 : /*---- gzip writer -------------------------------------------------*/
605 :
606 : #include <zlib.h>
607 :
608 : class WriterGzip : public WriterInterface
609 : {
610 : public:
611 0 : WriterGzip(LOG_CHN* log_chn, int compress) // ctor
612 0 : {
613 0 : if (fTrace)
614 0 : printf("WriterGzip: path [%s]\n", log_chn->path.c_str());
615 0 : fGzfp = 0;
616 0 : if (log_chn->settings.gzip_compression) {
617 0 : fCompress = log_chn->settings.gzip_compression;
618 : } else {
619 0 : fCompress = compress;
620 : }
621 0 : fLastCheckTime = time(NULL);
622 0 : }
623 :
624 0 : ~WriterGzip() // dtor
625 0 : {
626 0 : if (fTrace)
627 0 : printf("WriterGzip: destructor\n");
628 0 : assert(fGzfp == 0);
629 0 : }
630 :
631 0 : int wr_open(LOG_CHN* log_chn, int run_number)
632 : {
633 : int zerror;
634 :
635 0 : fBytesIn = 0;
636 0 : fBytesOut = 0;
637 :
638 0 : if (fTrace)
639 0 : printf("WriterGzip: open path [%s]\n", log_chn->path.c_str());
640 :
641 0 : assert(fGzfp == 0);
642 :
643 0 : if (check_file_exists(log_chn->path.c_str()))
644 0 : return SS_FILE_EXISTS;
645 :
646 0 : fGzfp = gzopen(log_chn->path.c_str(), "wb");
647 0 : if (fGzfp == 0) {
648 0 : cm_msg(MERROR, "WriterGzip::wr_open", "Cannot write to file \'%s\', gzopen() errno %d (%s)", log_chn->path.c_str(), errno, strerror(errno));
649 0 : return SS_FILE_ERROR;
650 : }
651 :
652 0 : chmod(log_chn->path.c_str(), 0444);
653 :
654 : //printf("WriterGzip::wr_open: compress %d\n", fCompress);
655 :
656 0 : if (fCompress) {
657 0 : zerror = gzsetparams(fGzfp, fCompress, Z_DEFAULT_STRATEGY);
658 0 : if (zerror != Z_OK) {
659 0 : cm_msg(MERROR, "WriterGzip::wr_open", "gzsetparams() zerror %d", zerror);
660 0 : return SS_FILE_ERROR;
661 : }
662 : }
663 :
664 : #if ZLIB_VERNUM > 0x1235
665 : // gzbuffer() added in zlib 1.2.3.5 (8 Jan 2010)
666 0 : zerror = gzbuffer(fGzfp, 128*1024);
667 0 : if (zerror != Z_OK) {
668 0 : cm_msg(MERROR, "WriterGzip::wr_open", "gzbuffer() zerror %d", zerror);
669 0 : return SS_FILE_ERROR;
670 : }
671 : #else
672 : #warning Very old zlib, no gzbuffer()!
673 : #endif
674 :
675 0 : log_chn->handle = 8888;
676 :
677 0 : fFilename = log_chn->path;
678 0 : return SUCCESS;
679 : }
680 :
681 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
682 : {
683 0 : if (fTrace)
684 0 : printf("WriterGzip: write path [%s], size %d\n", log_chn->path.c_str(), size);
685 :
686 0 : if (size == 0)
687 0 : return SUCCESS;
688 :
689 0 : assert(fGzfp);
690 :
691 0 : fBytesIn += size;
692 :
693 0 : int wr = gzwrite(fGzfp, data, size);
694 :
695 0 : if (wr != size) {
696 0 : cm_msg(MERROR, "WriterGzip::wr_write", "Cannot write to file \'%s\', gzwrite(%d) returned %d, errno: %d (%s)", log_chn->path.c_str(), size, wr, errno, strerror(errno));
697 0 : return SS_FILE_ERROR;
698 : }
699 :
700 : #if ZLIB_VERNUM > 0x1235
701 : // gzoffset() added in zlib 1.2.3.5 (8 Jan 2010)
702 0 : fBytesOut = gzoffset(fGzfp);
703 : #else
704 : #warning Very old zlib, no gzoffset()!
705 : time_t now = time(NULL);
706 : if (now - fLastCheckTime > 2) {
707 : fLastCheckTime = now;
708 : fBytesOut = ss_file_size(log_chn->path.c_str());
709 : }
710 : #endif
711 :
712 0 : return SUCCESS;
713 : }
714 :
715 0 : int wr_close(LOG_CHN* log_chn, int run_number)
716 : {
717 : int zerror;
718 :
719 0 : if (fTrace)
720 0 : printf("WriterGzip: close path [%s]\n", log_chn->path.c_str());
721 :
722 0 : assert(fGzfp);
723 :
724 0 : log_chn->handle = 0;
725 :
726 0 : zerror = gzflush(fGzfp, Z_FINISH);
727 :
728 0 : if (zerror != Z_OK) {
729 0 : cm_msg(MERROR, "WriterGzip::wr_close", "Cannot write to file \'%s\', gzflush(Z_FINISH) zerror %d, errno: %d (%s)", log_chn->path.c_str(), zerror, errno, strerror(errno));
730 0 : return SS_FILE_ERROR;
731 : }
732 :
733 0 : zerror = gzclose(fGzfp);
734 0 : fGzfp = 0;
735 :
736 0 : if (zerror != Z_OK) {
737 0 : cm_msg(MERROR, "WriterGzip::wr_close", "Cannot write to file \'%s\', gzclose() zerror %d, errno: %d (%s)", log_chn->path.c_str(), zerror, errno, strerror(errno));
738 0 : return SS_FILE_ERROR;
739 : }
740 :
741 0 : fBytesOut = ss_file_size(log_chn->path.c_str());
742 :
743 0 : return SUCCESS;
744 : }
745 :
746 0 : std::string wr_get_file_ext()
747 : {
748 0 : return ".gz";
749 : }
750 :
751 0 : std::string wr_get_chain()
752 : {
753 0 : return "gzip > " + fFilename;
754 : }
755 :
756 : private:
757 : std::string fFilename;
758 : gzFile fGzfp;
759 : int fCompress;
760 : time_t fLastCheckTime;
761 : };
762 :
763 : /*---- pipe writer -------------------------------------------------*/
764 :
765 : class WriterPopen : public WriterInterface
766 : {
767 : public:
768 0 : WriterPopen(LOG_CHN* log_chn, const char* pipe_command, const char* file_ext) // ctor
769 0 : {
770 0 : if (fTrace)
771 0 : printf("WriterPopen: path [%s]\n", log_chn->path.c_str());
772 0 : fFp = NULL;
773 0 : fPipeCommand = pipe_command;
774 0 : fFileExt = file_ext;
775 0 : fLastCheckTime = time(NULL);
776 0 : }
777 :
778 0 : ~WriterPopen() // dtor
779 0 : {
780 0 : if (fTrace)
781 0 : printf("WriterPopen: destructor\n");
782 0 : if (fFp)
783 0 : pclose(fFp);
784 0 : fFp = NULL;
785 0 : }
786 :
787 0 : int wr_open(LOG_CHN* log_chn, int run_number)
788 : {
789 0 : fBytesIn = 0;
790 0 : fBytesOut = 0;
791 :
792 0 : if (fTrace)
793 0 : printf("WriterPopen: open path [%s] pipe [%s] ext [%s]\n", log_chn->path.c_str(), fPipeCommand.c_str(), fFileExt.c_str());
794 :
795 0 : assert(fFp == NULL);
796 :
797 0 : if (check_file_exists(log_chn->path.c_str()))
798 0 : return SS_FILE_EXISTS;
799 :
800 : #ifdef OS_WINNT
801 : // sorry no popen?!?
802 : return SS_FILE_ERROR;
803 : #else
804 0 : fCommand = fPipeCommand + log_chn->path;
805 :
806 0 : fFp = popen(fCommand.c_str(), "w");
807 0 : if (fFp == NULL) {
808 0 : cm_msg(MERROR, "WriterPopen::wr_open", "Cannot write to pipe \'%s\', popen() errno %d (%s)", fCommand.c_str(), errno, strerror(errno));
809 0 : return SS_FILE_ERROR;
810 : }
811 :
812 0 : log_chn->handle = 9999;
813 :
814 0 : return SUCCESS;
815 : #endif
816 : }
817 :
818 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
819 : {
820 0 : if (fTrace)
821 0 : printf("WriterPopen: write path [%s], size %d\n", log_chn->path.c_str(), size);
822 :
823 0 : if (size == 0)
824 0 : return SUCCESS;
825 :
826 0 : if (fFp == NULL) {
827 0 : return SS_FILE_ERROR;
828 : }
829 :
830 0 : fBytesIn += size;
831 :
832 0 : int wr = fwrite(data, 1, size, fFp);
833 :
834 : //if (wr > 0)
835 : //fBytesOut += wr;
836 :
837 0 : if (wr != size) {
838 0 : cm_msg(MERROR, "WriterPopen::wr_write", "Cannot write to pipe \'%s\', fwrite(%d) returned %d, errno %d (%s)", fCommand.c_str(), size, wr, errno, strerror(errno));
839 :
840 0 : if (errno == EPIPE) {
841 0 : cm_msg(MERROR, "WriterPopen::wr_write", "Cannot write to pipe \'%s\': broken pipe, closing the pipe", fCommand.c_str());
842 0 : wr_close(log_chn, 0);
843 : }
844 :
845 0 : return SS_FILE_ERROR;
846 : }
847 :
848 0 : time_t now = time(NULL);
849 0 : if (now - fLastCheckTime > 2) {
850 0 : fLastCheckTime = now;
851 0 : fBytesOut = ss_file_size(log_chn->path.c_str());
852 : }
853 :
854 0 : return SUCCESS;
855 : }
856 :
857 0 : int wr_close(LOG_CHN* log_chn, int run_number)
858 : {
859 : int err;
860 :
861 0 : if (fTrace)
862 0 : printf("WriterPopen: close path [%s]\n", log_chn->path.c_str());
863 :
864 0 : assert(fFp != NULL);
865 :
866 0 : log_chn->handle = 0;
867 :
868 : #ifdef OS_WINNT
869 : // sorry no popen?!?
870 : return SS_FILE_ERROR;
871 : #else
872 0 : err = pclose(fFp);
873 0 : fFp = NULL;
874 :
875 0 : if (err != 0) {
876 0 : cm_msg(MERROR, "WriterPopen::wr_close", "Cannot write to pipe \'%s\', pclose() returned %d, errno %d (%s)", fCommand.c_str(), err, errno, strerror(errno));
877 0 : return SS_FILE_ERROR;
878 : }
879 :
880 0 : chmod(log_chn->path.c_str(), 0444);
881 :
882 0 : fBytesOut = ss_file_size(log_chn->path.c_str());
883 :
884 0 : return SUCCESS;
885 : #endif
886 : }
887 :
888 0 : std::string wr_get_file_ext()
889 : {
890 0 : return fFileExt;
891 : }
892 :
893 0 : std::string wr_get_chain()
894 : {
895 0 : return fPipeCommand;
896 : }
897 :
898 : private:
899 : FILE* fFp;
900 : std::string fPipeCommand;
901 : std::string fCommand;
902 : std::string fFileExt;
903 : time_t fLastCheckTime;
904 : };
905 :
906 : /*---- CRC32-ZLIB computation --------------------------------------*/
907 :
908 : #include <zlib.h>
909 :
910 : class WriterCRC32Zlib : public WriterInterface
911 : {
912 : public:
913 0 : WriterCRC32Zlib(LOG_CHN* log_chn, int level, WriterInterface* wr) // ctor
914 0 : {
915 0 : if (fTrace)
916 0 : printf("WriterCRC32Zlib: path [%s], level %d\n", log_chn->path.c_str(), level);
917 :
918 0 : assert(wr != NULL);
919 :
920 0 : fLevel = level;
921 0 : fWr = wr;
922 0 : fCrc32 = 0;
923 0 : }
924 :
925 0 : ~WriterCRC32Zlib() // dtor
926 0 : {
927 0 : if (fTrace)
928 0 : printf("WriterCRC32Zlib: destructor\n");
929 0 : DELETE(fWr);
930 0 : }
931 :
932 0 : int wr_open(LOG_CHN* log_chn, int run_number)
933 : {
934 : int status;
935 :
936 0 : if (fTrace)
937 0 : printf("WriterCRC32Zlib: open path [%s], level %d\n", log_chn->path.c_str(), fLevel);
938 :
939 0 : status = fWr->wr_open(log_chn, run_number);
940 :
941 0 : fBytesIn += 0;
942 0 : fBytesOut = fWr->fBytesOut;
943 :
944 0 : if (status != SUCCESS) {
945 0 : return status;
946 : }
947 :
948 0 : log_chn->handle = 9999;
949 :
950 0 : fCrc32 = crc32(0, Z_NULL, 0);
951 :
952 0 : return SUCCESS;
953 : }
954 :
955 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
956 : {
957 0 : if (fTrace)
958 0 : printf("WriterCRC32Zlib: write path [%s], size %d\n", log_chn->path.c_str(), size);
959 :
960 0 : fCrc32 = crc32(fCrc32, (const Bytef*)data, size);
961 :
962 0 : int status = fWr->wr_write(log_chn, data, size);
963 :
964 0 : fBytesIn += size;
965 0 : fBytesOut = fWr->fBytesOut;
966 :
967 0 : if (status != SUCCESS) {
968 0 : return status;
969 : }
970 :
971 0 : return SUCCESS;
972 : }
973 :
974 0 : int wr_close(LOG_CHN* log_chn, int run_number)
975 : {
976 0 : std::string x = xpathname(log_chn->path.c_str(), fLevel);
977 0 : std::string f = x + ".crc32zlib";
978 :
979 0 : if (fTrace)
980 0 : printf("WriterCRC32Zlib: close path [%s], level %d, file [%s]\n", log_chn->path.c_str(), fLevel, f.c_str());
981 :
982 0 : log_chn->handle = 0;
983 :
984 0 : cm_msg(MLOG, "CRC32Zlib", "File \'%s\' CRC32-zlib checksum: 0x%08lx, %.0f bytes", x.c_str(), (unsigned long)fCrc32, fBytesIn);
985 :
986 0 : FILE *fp = fopen_wx(f.c_str());
987 0 : if (!fp) {
988 0 : cm_msg(MERROR, "WriterCRC32Zlib::wr_close", "Cannot write CRC32Zlib to file \'%s\', fopen() errno %d (%s)", f.c_str(), errno, strerror(errno));
989 : } else {
990 0 : fprintf(fp, "%08lx %.0f %s\n", (unsigned long)fCrc32, fBytesIn, x.c_str());
991 0 : fclose(fp);
992 : }
993 :
994 : /* close downstream writer */
995 :
996 0 : int status = fWr->wr_close(log_chn, run_number);
997 :
998 0 : fBytesIn += 0;
999 0 : fBytesOut = fWr->fBytesOut;
1000 :
1001 0 : if (status != SUCCESS) {
1002 0 : return status;
1003 : }
1004 :
1005 0 : return SUCCESS;
1006 0 : }
1007 :
1008 0 : std::string wr_get_file_ext() {
1009 0 : return fWr->wr_get_file_ext();
1010 : }
1011 :
1012 0 : std::string wr_get_chain() {
1013 0 : return "CRC32ZLIB | " + fWr->wr_get_chain();
1014 : }
1015 :
1016 : private:
1017 : int fLevel;
1018 : WriterInterface *fWr;
1019 : uLong fCrc32;
1020 : };
1021 :
1022 : /*---- CRC32C computation ------------------------------------------*/
1023 :
1024 : #include "crc32c.h"
1025 :
1026 : class WriterCRC32C : public WriterInterface
1027 : {
1028 : public:
1029 0 : WriterCRC32C(LOG_CHN* log_chn, int level, WriterInterface* wr) // ctor
1030 0 : {
1031 0 : if (fTrace)
1032 0 : printf("WriterCRC32C: path [%s], level %d\n", log_chn->path.c_str(), level);
1033 :
1034 0 : assert(wr != NULL);
1035 :
1036 0 : fLevel = level;
1037 0 : fWr = wr;
1038 0 : fCrc32 = 0;
1039 0 : }
1040 :
1041 0 : ~WriterCRC32C() // dtor
1042 0 : {
1043 0 : if (fTrace)
1044 0 : printf("WriterCRC32C: destructor\n");
1045 0 : DELETE(fWr);
1046 0 : }
1047 :
1048 0 : int wr_open(LOG_CHN* log_chn, int run_number)
1049 : {
1050 : int status;
1051 :
1052 0 : if (fTrace)
1053 0 : printf("WriterCRC32C: open path [%s], level %d\n", log_chn->path.c_str(), fLevel);
1054 :
1055 0 : status = fWr->wr_open(log_chn, run_number);
1056 :
1057 0 : fBytesIn += 0;
1058 0 : fBytesOut = fWr->fBytesOut;
1059 :
1060 0 : if (status != SUCCESS) {
1061 0 : return status;
1062 : }
1063 :
1064 0 : log_chn->handle = 9999;
1065 :
1066 0 : fCrc32 = 0;
1067 :
1068 0 : return SUCCESS;
1069 : }
1070 :
1071 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
1072 : {
1073 0 : if (fTrace)
1074 0 : printf("WriterCRC32C: write path [%s], size %d\n", log_chn->path.c_str(), size);
1075 :
1076 0 : fCrc32 = crc32c(fCrc32, data, size);
1077 :
1078 0 : int status = fWr->wr_write(log_chn, data, size);
1079 :
1080 0 : fBytesIn += size;
1081 0 : fBytesOut = fWr->fBytesOut;
1082 :
1083 0 : if (status != SUCCESS) {
1084 0 : return status;
1085 : }
1086 :
1087 0 : return SUCCESS;
1088 : }
1089 :
1090 0 : int wr_close(LOG_CHN* log_chn, int run_number)
1091 : {
1092 0 : std::string x = xpathname(log_chn->path.c_str(), fLevel);
1093 0 : std::string f = x + ".crc32c";
1094 :
1095 0 : if (fTrace)
1096 0 : printf("WriterCRC32C: close path [%s], level %d, file [%s]\n", log_chn->path.c_str(), fLevel, f.c_str());
1097 :
1098 0 : log_chn->handle = 0;
1099 :
1100 0 : cm_msg(MLOG, "CRC32C", "File \'%s\' CRC32C checksum: 0x%08lx, %.0f bytes", x.c_str(), (unsigned long)fCrc32, fBytesIn);
1101 :
1102 0 : FILE *fp = fopen_wx(f.c_str());
1103 0 : if (!fp) {
1104 0 : cm_msg(MERROR, "WriterCRC32C::wr_close", "Cannot write CRC32C to file \'%s\', fopen() errno %d (%s)", f.c_str(), errno, strerror(errno));
1105 : } else {
1106 0 : fprintf(fp, "%08lx %.0f %s\n", (unsigned long)fCrc32, fBytesIn, x.c_str());
1107 0 : fclose(fp);
1108 : }
1109 :
1110 : /* close downstream writer */
1111 :
1112 0 : int status = fWr->wr_close(log_chn, run_number);
1113 :
1114 0 : fBytesIn += 0;
1115 0 : fBytesOut = fWr->fBytesOut;
1116 :
1117 0 : if (status != SUCCESS) {
1118 0 : return status;
1119 : }
1120 :
1121 0 : return SUCCESS;
1122 0 : }
1123 :
1124 0 : std::string wr_get_file_ext() {
1125 0 : return fWr->wr_get_file_ext();
1126 : }
1127 :
1128 0 : std::string wr_get_chain() {
1129 0 : return "CRC32C | " + fWr->wr_get_chain();
1130 : }
1131 :
1132 : private:
1133 : int fLevel;
1134 : WriterInterface *fWr;
1135 : uint32_t fCrc32;
1136 : };
1137 :
1138 : /*---- SHA-256 computation -----------------------------------------*/
1139 :
1140 : #include "sha256.h"
1141 :
1142 : class WriterSHA256 : public WriterInterface
1143 : {
1144 : public:
1145 0 : WriterSHA256(LOG_CHN* log_chn, int level, WriterInterface* wr) // ctor
1146 0 : {
1147 0 : if (fTrace)
1148 0 : printf("WriterSHA256: path [%s], level %d\n", log_chn->path.c_str(), level);
1149 :
1150 0 : assert(wr != NULL);
1151 :
1152 0 : fLevel = level;
1153 0 : fWr = wr;
1154 :
1155 0 : mbedtls_sha256_init(&fCtx);
1156 0 : }
1157 :
1158 0 : ~WriterSHA256() // dtor
1159 0 : {
1160 0 : if (fTrace)
1161 0 : printf("WriterSHA256: destructor\n");
1162 0 : DELETE(fWr);
1163 :
1164 0 : mbedtls_sha256_free(&fCtx);
1165 0 : }
1166 :
1167 0 : int wr_open(LOG_CHN* log_chn, int run_number)
1168 : {
1169 : int status;
1170 :
1171 0 : if (fTrace)
1172 0 : printf("WriterSHA256: open path [%s], level %d\n", log_chn->path.c_str(), fLevel);
1173 :
1174 0 : status = fWr->wr_open(log_chn, run_number);
1175 :
1176 0 : fBytesIn += 0;
1177 0 : fBytesOut = fWr->fBytesOut;
1178 :
1179 0 : if (status != SUCCESS) {
1180 0 : return status;
1181 : }
1182 :
1183 0 : log_chn->handle = 9999;
1184 :
1185 0 : mbedtls_sha256_starts(&fCtx, 0); // 2nd argument selects 0=SHA-256 vs 1=SHA-224
1186 :
1187 0 : return SUCCESS;
1188 : }
1189 :
1190 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
1191 : {
1192 0 : if (fTrace)
1193 0 : printf("WriterSHA256: write path [%s], size %d\n", log_chn->path.c_str(), size);
1194 :
1195 0 : mbedtls_sha256_update(&fCtx, (const unsigned char*)data, size);
1196 :
1197 0 : int status = fWr->wr_write(log_chn, data, size);
1198 :
1199 0 : fBytesIn += size;
1200 0 : fBytesOut = fWr->fBytesOut;
1201 :
1202 0 : if (status != SUCCESS) {
1203 0 : return status;
1204 : }
1205 :
1206 0 : return SUCCESS;
1207 : }
1208 :
1209 0 : std::string toHex(unsigned char c)
1210 : {
1211 : char s[3];
1212 0 : sprintf(s, "%02x", c);
1213 0 : return s;
1214 : }
1215 :
1216 0 : std::string toString(const unsigned char sha256sum[32])
1217 : {
1218 0 : std::string s;
1219 0 : for (int i=0; i<32; i++)
1220 0 : s += toHex(sha256sum[i]);
1221 0 : return s;
1222 0 : }
1223 :
1224 0 : int wr_close(LOG_CHN* log_chn, int run_number)
1225 : {
1226 0 : std::string x = xpathname(log_chn->path.c_str(), fLevel);
1227 0 : std::string f = x + ".sha256";
1228 :
1229 0 : if (fTrace)
1230 0 : printf("WriterSHA256: close path [%s], level %d, file [%s]\n", log_chn->path.c_str(), fLevel, f.c_str());
1231 :
1232 0 : log_chn->handle = 0;
1233 :
1234 : unsigned char sha256sum[32];
1235 0 : mbedtls_sha256_finish(&fCtx, sha256sum);
1236 :
1237 : //std::string s = toString(sha256sum);
1238 : //printf("sha256 %s\n", s.c_str());
1239 :
1240 0 : cm_msg(MLOG, "SHA256", "File \'%s\' SHA-256 checksum: %s, %.0f bytes", x.c_str(), toString(sha256sum).c_str(), fBytesIn);
1241 :
1242 0 : FILE *fp = fopen_wx(f.c_str());
1243 0 : if (!fp) {
1244 0 : cm_msg(MERROR, "WriterSHA256::wr_close", "Cannot write SHA-256 checksum to file \'%s\', fopen() errno %d (%s)", f.c_str(), errno, strerror(errno));
1245 : } else {
1246 0 : fprintf(fp, "%s %.0f %s\n", toString(sha256sum).c_str(), fBytesIn, x.c_str());
1247 0 : fclose(fp);
1248 : }
1249 :
1250 : /* close downstream writer */
1251 :
1252 0 : int status = fWr->wr_close(log_chn, run_number);
1253 :
1254 0 : fBytesIn += 0;
1255 0 : fBytesOut = fWr->fBytesOut;
1256 :
1257 0 : if (status != SUCCESS) {
1258 0 : return status;
1259 : }
1260 :
1261 0 : return SUCCESS;
1262 0 : }
1263 :
1264 0 : std::string wr_get_file_ext() {
1265 0 : return fWr->wr_get_file_ext();
1266 : }
1267 :
1268 0 : std::string wr_get_chain() {
1269 0 : return "SHA256 | " + fWr->wr_get_chain();
1270 : }
1271 :
1272 : private:
1273 : int fLevel;
1274 : WriterInterface *fWr;
1275 : mbedtls_sha256_context fCtx;
1276 : };
1277 :
1278 : /*---- SHA-512 computation -----------------------------------------*/
1279 :
1280 : #include "sha512.h"
1281 :
1282 : class WriterSHA512 : public WriterInterface
1283 : {
1284 : public:
1285 0 : WriterSHA512(LOG_CHN* log_chn, int level, WriterInterface* wr) // ctor
1286 0 : {
1287 0 : if (fTrace)
1288 0 : printf("WriterSHA512: path [%s], level %d\n", log_chn->path.c_str(), level);
1289 :
1290 0 : assert(wr != NULL);
1291 :
1292 0 : fLevel = level;
1293 0 : fWr = wr;
1294 :
1295 0 : mbedtls_sha512_init(&fCtx);
1296 0 : }
1297 :
1298 0 : ~WriterSHA512() // dtor
1299 0 : {
1300 0 : if (fTrace)
1301 0 : printf("WriterSHA512: destructor\n");
1302 0 : DELETE(fWr);
1303 :
1304 0 : mbedtls_sha512_free(&fCtx);
1305 0 : }
1306 :
1307 0 : int wr_open(LOG_CHN* log_chn, int run_number)
1308 : {
1309 : int status;
1310 :
1311 0 : if (fTrace)
1312 0 : printf("WriterSHA512: open path [%s], level %d\n", log_chn->path.c_str(), fLevel);
1313 :
1314 0 : status = fWr->wr_open(log_chn, run_number);
1315 :
1316 0 : fBytesIn += 0;
1317 0 : fBytesOut = fWr->fBytesOut;
1318 :
1319 0 : if (status != SUCCESS) {
1320 0 : return status;
1321 : }
1322 :
1323 0 : log_chn->handle = 9999;
1324 :
1325 0 : mbedtls_sha512_starts(&fCtx, 0); // 2nd argument selects 0=SHA-512 vs 1=SHA-384
1326 :
1327 0 : return SUCCESS;
1328 : }
1329 :
1330 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
1331 : {
1332 0 : if (fTrace)
1333 0 : printf("WriterSHA512: write path [%s], size %d\n", log_chn->path.c_str(), size);
1334 :
1335 0 : mbedtls_sha512_update(&fCtx, (const unsigned char*)data, size);
1336 :
1337 0 : int status = fWr->wr_write(log_chn, data, size);
1338 :
1339 0 : fBytesIn += size;
1340 0 : fBytesOut = fWr->fBytesOut;
1341 :
1342 0 : if (status != SUCCESS) {
1343 0 : return status;
1344 : }
1345 :
1346 0 : return SUCCESS;
1347 : }
1348 :
1349 0 : std::string toHex(unsigned char c)
1350 : {
1351 : char s[3];
1352 0 : sprintf(s, "%02x", c);
1353 0 : return s;
1354 : }
1355 :
1356 0 : std::string toString(const unsigned char sha512sum[64])
1357 : {
1358 0 : std::string s;
1359 0 : for (int i=0; i<64; i++)
1360 0 : s += toHex(sha512sum[i]);
1361 0 : return s;
1362 0 : }
1363 :
1364 0 : int wr_close(LOG_CHN* log_chn, int run_number)
1365 : {
1366 0 : std::string x = xpathname(log_chn->path.c_str(), fLevel);
1367 0 : std::string f = x + ".sha512";
1368 :
1369 0 : if (fTrace)
1370 0 : printf("WriterSHA512: close path [%s], level %d, file [%s]\n", log_chn->path.c_str(), fLevel, f.c_str());
1371 :
1372 0 : log_chn->handle = 0;
1373 :
1374 : unsigned char sha512sum[64];
1375 0 : mbedtls_sha512_finish(&fCtx, sha512sum);
1376 :
1377 : //std::string s = toString(sha512sum);
1378 : //printf("sha512 %s\n", s.c_str());
1379 :
1380 0 : cm_msg(MLOG, "SHA512", "File \'%s\' SHA-512 checksum: %s, %.0f bytes", x.c_str(), toString(sha512sum).c_str(), fBytesIn);
1381 :
1382 0 : FILE *fp = fopen_wx(f.c_str());
1383 0 : if (!fp) {
1384 0 : cm_msg(MERROR, "WriterSHA512::wr_close", "Cannot write SHA-512 checksum to file \'%s\', fopen() errno %d (%s)", f.c_str(), errno, strerror(errno));
1385 : } else {
1386 0 : fprintf(fp, "%s %.0f %s\n", toString(sha512sum).c_str(), fBytesIn, x.c_str());
1387 0 : fclose(fp);
1388 : }
1389 :
1390 : /* close downstream writer */
1391 :
1392 0 : int status = fWr->wr_close(log_chn, run_number);
1393 :
1394 0 : fBytesIn += 0;
1395 0 : fBytesOut = fWr->fBytesOut;
1396 :
1397 0 : if (status != SUCCESS) {
1398 0 : return status;
1399 : }
1400 :
1401 0 : return SUCCESS;
1402 0 : }
1403 :
1404 0 : std::string wr_get_file_ext() {
1405 0 : return fWr->wr_get_file_ext();
1406 : }
1407 :
1408 0 : std::string wr_get_chain() {
1409 0 : return "SHA512 | " + fWr->wr_get_chain();
1410 : }
1411 :
1412 : private:
1413 : int fLevel;
1414 : WriterInterface *fWr;
1415 : mbedtls_sha512_context fCtx;
1416 : };
1417 :
1418 : /*---- LZ4 compressed writer --------------------------------------*/
1419 :
1420 : #include "mlz4frame.h"
1421 :
1422 : class WriterLZ4 : public WriterInterface
1423 : {
1424 : public:
1425 0 : WriterLZ4(LOG_CHN* log_chn, WriterInterface* wr) // ctor
1426 0 : {
1427 0 : if (fTrace)
1428 0 : printf("WriterLZ4: path [%s]\n", log_chn->path.c_str());
1429 :
1430 0 : assert(wr != NULL);
1431 :
1432 0 : fBuffer = NULL;
1433 0 : fWr = wr;
1434 0 : fBufferSize = 0;
1435 0 : fBlockSize = 0;
1436 0 : }
1437 :
1438 0 : ~WriterLZ4() // dtor
1439 0 : {
1440 0 : if (fTrace)
1441 0 : printf("WriterLZ4: destructor\n");
1442 :
1443 0 : FREE(fBuffer);
1444 0 : DELETE(fWr);
1445 0 : }
1446 :
1447 0 : int wr_open(LOG_CHN* log_chn, int run_number)
1448 : {
1449 : int status;
1450 : MLZ4F_errorCode_t errorCode;
1451 :
1452 0 : if (fTrace)
1453 0 : printf("WriterLZ4: open path [%s]\n", log_chn->path.c_str());
1454 :
1455 0 : status = fWr->wr_open(log_chn, run_number);
1456 0 : if (status != SUCCESS) {
1457 0 : return status;
1458 : }
1459 :
1460 0 : errorCode = MLZ4F_createCompressionContext(&fContext, MLZ4F_VERSION);
1461 0 : if (MLZ4F_isError(errorCode)) {
1462 0 : cm_msg(MERROR, "WriterLZ4::wr_open", "LZ4F_createCompressionContext() error %d (%s)", (int)errorCode, MLZ4F_getErrorName(errorCode));
1463 0 : return SS_FILE_ERROR;
1464 : }
1465 :
1466 0 : MLZ4F_blockSizeID_t blockSizeId = MLZ4F_max4MB;
1467 0 : fBlockSize = 4*1024*1024;
1468 0 : fBufferSize = MLZ4F_compressFrameBound(fBlockSize, NULL);
1469 0 : fBufferSize *= 2; // kludge
1470 0 : fBuffer = (char*)malloc(fBufferSize);
1471 0 : if (fBuffer == NULL) {
1472 0 : cm_msg(MERROR, "WriterLZ4::wr_open", "Cannot malloc() %d bytes for an LZ4 compression buffer, block size %d, errno %d (%s)", fBufferSize, fBlockSize, errno, strerror(errno));
1473 0 : return SS_FILE_ERROR;
1474 : }
1475 :
1476 0 : MEMZERO(fPrefs);
1477 :
1478 0 : fPrefs.compressionLevel = 0; // 0=fast, non-zero=???
1479 0 : fPrefs.autoFlush = 0; // ???
1480 0 : fPrefs.frameInfo.contentChecksumFlag = MLZ4F_contentChecksumEnabled;
1481 0 : fPrefs.frameInfo.blockSizeID = blockSizeId;
1482 :
1483 0 : size_t headerSize = MLZ4F_compressBegin(fContext, fBuffer, fBufferSize, &fPrefs);
1484 :
1485 0 : if (MLZ4F_isError(headerSize)) {
1486 0 : errorCode = headerSize;
1487 0 : cm_msg(MERROR, "WriterLZ4::wr_open", "LZ4F_compressBegin() error %d (%s)", (int)errorCode, MLZ4F_getErrorName(errorCode));
1488 0 : return SS_FILE_ERROR;
1489 : }
1490 :
1491 0 : status = fWr->wr_write(log_chn, fBuffer, headerSize);
1492 :
1493 0 : fBytesIn += 0;
1494 0 : fBytesOut = fWr->fBytesOut;
1495 :
1496 0 : if (status != SUCCESS) {
1497 0 : return SS_FILE_ERROR;
1498 : }
1499 :
1500 0 : log_chn->handle = 9999;
1501 :
1502 0 : return SUCCESS;
1503 : }
1504 :
1505 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
1506 : {
1507 0 : const char* ptr = (const char*)data;
1508 0 : int remaining = size;
1509 :
1510 0 : if (fTrace)
1511 0 : printf("WriterLZ4: write path [%s], size %d\n", log_chn->path.c_str(), size);
1512 :
1513 0 : while (remaining > 0) {
1514 0 : int wsize = remaining;
1515 :
1516 0 : if (wsize > fBlockSize)
1517 0 : wsize = fBlockSize;
1518 :
1519 0 : size_t outSize = MLZ4F_compressUpdate(fContext, fBuffer, fBufferSize, ptr, wsize, NULL);
1520 :
1521 0 : if (MLZ4F_isError(outSize)) {
1522 0 : int errorCode = outSize;
1523 0 : cm_msg(MERROR, "WriterLZ4::wr_write", "LZ4F_compressUpdate() with %d bytes, block size %d, buffer size %d, write size %d, remaining %d bytes, error %d (%s)", wsize, fBlockSize, fBufferSize, size, remaining, (int)errorCode, MLZ4F_getErrorName(errorCode));
1524 0 : return SS_FILE_ERROR;
1525 : }
1526 :
1527 0 : if (outSize > 0) {
1528 0 : int status = fWr->wr_write(log_chn, fBuffer, outSize);
1529 :
1530 0 : fBytesIn += wsize;
1531 0 : fBytesOut = fWr->fBytesOut;
1532 :
1533 0 : if (status != SUCCESS) {
1534 0 : return SS_FILE_ERROR;
1535 : }
1536 : }
1537 :
1538 0 : ptr += wsize;
1539 0 : remaining -= wsize;
1540 : }
1541 :
1542 0 : return SUCCESS;
1543 : }
1544 :
1545 0 : int wr_close(LOG_CHN* log_chn, int run_number)
1546 : {
1547 0 : int xstatus = SUCCESS;
1548 : MLZ4F_errorCode_t errorCode;
1549 :
1550 0 : if (fTrace)
1551 0 : printf("WriterLZ4: close path [%s]\n", log_chn->path.c_str());
1552 :
1553 0 : log_chn->handle = 0;
1554 :
1555 : /* write End of Stream mark */
1556 0 : size_t headerSize = MLZ4F_compressEnd(fContext, fBuffer, fBufferSize, NULL);
1557 :
1558 0 : if (MLZ4F_isError(headerSize)) {
1559 0 : errorCode = headerSize;
1560 0 : cm_msg(MERROR, "WriterLZ4::wr_close", "LZ4F_compressEnd() error %d (%s)", (int)errorCode, MLZ4F_getErrorName(errorCode));
1561 0 : return SS_FILE_ERROR;
1562 : }
1563 :
1564 0 : int status = fWr->wr_write(log_chn, fBuffer, headerSize);
1565 :
1566 0 : fBytesIn += 0;
1567 0 : fBytesOut = fWr->fBytesOut;
1568 :
1569 0 : if (status != SUCCESS) {
1570 0 : if (xstatus == SUCCESS)
1571 0 : xstatus = status;
1572 : }
1573 :
1574 : /* close downstream writer */
1575 :
1576 0 : status = fWr->wr_close(log_chn, run_number);
1577 :
1578 0 : if (status != SUCCESS) {
1579 0 : if (xstatus == SUCCESS)
1580 0 : xstatus = status;
1581 : }
1582 :
1583 : /* free resources */
1584 :
1585 0 : free(fBuffer);
1586 0 : fBuffer = NULL;
1587 0 : fBufferSize = 0;
1588 :
1589 0 : errorCode = MLZ4F_freeCompressionContext(fContext);
1590 0 : if (MLZ4F_isError(errorCode)) {
1591 0 : cm_msg(MERROR, "WriterLZ4::wr_close", "LZ4F_freeCompressionContext() error %d (%s)", (int)errorCode, MLZ4F_getErrorName(errorCode));
1592 0 : if (xstatus == SUCCESS)
1593 0 : xstatus = SS_FILE_ERROR;
1594 : }
1595 :
1596 0 : return xstatus;
1597 : }
1598 :
1599 0 : std::string wr_get_file_ext() {
1600 0 : return ".lz4" + fWr->wr_get_file_ext();
1601 : }
1602 :
1603 0 : std::string wr_get_chain() {
1604 0 : return "lz4 | " + fWr->wr_get_chain();
1605 : }
1606 :
1607 : private:
1608 : WriterInterface *fWr;
1609 : MLZ4F_compressionContext_t fContext;
1610 : MLZ4F_preferences_t fPrefs;
1611 : char* fBuffer;
1612 : int fBufferSize;
1613 : int fBlockSize;
1614 : };
1615 :
1616 : /*---- Logging initialization --------------------------------------*/
1617 :
1618 0 : void logger_init()
1619 : {
1620 : INT size, status, delay;
1621 : BOOL flag;
1622 : HNDLE hKey, hKeyChannel;
1623 : KEY key;
1624 0 : std::string str;
1625 :
1626 : /*---- create /logger entries -----*/
1627 :
1628 0 : str = cm_get_path();
1629 0 : db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &str, TRUE);
1630 :
1631 0 : str = "";
1632 0 : db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &str, TRUE);
1633 :
1634 0 : str = "";
1635 0 : db_get_value_string(hDB, 0, "/Logger/History dir", 0, &str, TRUE);
1636 :
1637 0 : str = "";
1638 0 : db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &str, TRUE);
1639 :
1640 0 : size = sizeof(BOOL);
1641 0 : flag = TRUE;
1642 0 : db_get_value(hDB, 0, "/Logger/Write data", &flag, &size, TID_BOOL, TRUE);
1643 :
1644 0 : flag = FALSE;
1645 0 : db_get_value(hDB, 0, "/Logger/ODB Dump", &flag, &size, TID_BOOL, TRUE);
1646 :
1647 0 : str = "run%05d.json";
1648 0 : db_get_value_string(hDB, 0, "/Logger/ODB Dump File", 0, &str, TRUE);
1649 :
1650 0 : str = "last.json";
1651 0 : db_get_value_string(hDB, 0, "/Logger/ODB Last Dump File", 0, &str, TRUE);
1652 :
1653 0 : flag = FALSE;
1654 0 : size = sizeof(BOOL);
1655 0 : db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
1656 :
1657 0 : delay = 0;
1658 0 : size = sizeof(INT);
1659 0 : db_get_value(hDB, 0, "/Logger/Auto restart delay", &delay, &size, TID_INT32, TRUE);
1660 :
1661 0 : flag = TRUE;
1662 0 : db_get_value(hDB, 0, "/Logger/Tape message", &flag, &size, TID_BOOL, TRUE);
1663 :
1664 : /* create at least one logging channel */
1665 0 : status = db_find_key(hDB, 0, "/Logger/Channels/0", &hKey);
1666 0 : if (status != DB_SUCCESS) {
1667 : /* if no channels are defined, define at least one */
1668 0 : status = db_create_record(hDB, 0, "/Logger/Channels/0", strcomb1(chn_tree_str).c_str());
1669 0 : if (status != DB_SUCCESS)
1670 0 : cm_msg(MERROR, "logger_init", "Cannot create channel entry in database");
1671 : } else {
1672 : /* check format of other channels */
1673 0 : status = db_find_key(hDB, 0, "/Logger/Channels", &hKey);
1674 0 : if (status == DB_SUCCESS) {
1675 0 : for (int index = 0; ; index++) {
1676 0 : status = db_enum_key(hDB, hKey, index, &hKeyChannel);
1677 0 : if (status == DB_NO_MORE_SUBKEYS)
1678 0 : break;
1679 :
1680 0 : db_get_key(hDB, hKeyChannel, &key);
1681 0 : status = db_check_record(hDB, hKey, key.name, strcomb1(chn_tree_str).c_str(), TRUE);
1682 0 : if (status != DB_SUCCESS && status != DB_OPEN_RECORD) {
1683 0 : cm_msg(MERROR, "logger_init", "Cannot create/check channel record %s, db_check_record() status %d", key.name, status);
1684 0 : break;
1685 : }
1686 : }
1687 : }
1688 : }
1689 : #ifdef HAVE_MYSQL
1690 0 : create_runlog_sql_tree();
1691 : #endif
1692 0 : create_runlog_ascii_tree();
1693 0 : create_runlog_json_tree();
1694 0 : }
1695 :
1696 : /*---- ODB dump routine --------------------------------------------*/
1697 :
1698 0 : void log_odb_dump_odb(LOG_CHN * log_chn, short int event_id, INT run_number)
1699 : {
1700 : /* write ODB dump */
1701 :
1702 : static int buffer_size = 100000;
1703 :
1704 : do {
1705 0 : EVENT_HEADER* pevent = (EVENT_HEADER *) malloc(buffer_size);
1706 0 : if (pevent == NULL) {
1707 0 : cm_msg(MERROR, "log_odb_dump", "Cannot allocate ODB dump buffer");
1708 0 : break;
1709 : }
1710 :
1711 0 : int size = buffer_size - sizeof(EVENT_HEADER);
1712 : //int status = db_copy_xml(hDB, 0, (char *) (pevent + 1), &size);
1713 0 : int status = db_copy(hDB, 0, (char *) (pevent + 1), &size, "");
1714 0 : if (status != DB_TRUNCATED) {
1715 0 : bm_compose_event(pevent, event_id, MIDAS_MAGIC, buffer_size - sizeof(EVENT_HEADER) - size + 1, run_number);
1716 0 : log_write(log_chn, pevent);
1717 0 : free(pevent);
1718 0 : break;
1719 : }
1720 :
1721 : /* increase buffer size if truncated */
1722 0 : free(pevent);
1723 0 : buffer_size *= 10;
1724 0 : } while (1);
1725 0 : }
1726 :
1727 0 : void log_odb_dump_xml(LOG_CHN * log_chn, short int event_id, INT run_number)
1728 : {
1729 : /* write ODB dump */
1730 :
1731 : static int buffer_size = 100000;
1732 :
1733 : do {
1734 0 : EVENT_HEADER* pevent = (EVENT_HEADER *) malloc(buffer_size);
1735 0 : if (pevent == NULL) {
1736 0 : cm_msg(MERROR, "log_odb_dump", "Cannot allocate ODB dump buffer");
1737 0 : break;
1738 : }
1739 :
1740 0 : int size = buffer_size - sizeof(EVENT_HEADER);
1741 0 : int status = db_copy_xml(hDB, 0, (char *) (pevent + 1), &size, true);
1742 :
1743 : /* following line would dump ODB in old ASCII format instead of XML */
1744 : //status = db_copy(hDB, 0, (char *) (pevent + 1), &size, "");
1745 0 : if (status != DB_TRUNCATED) {
1746 0 : bm_compose_event(pevent, event_id, MIDAS_MAGIC, size, run_number);
1747 0 : log_write(log_chn, pevent);
1748 0 : free(pevent);
1749 0 : break;
1750 : }
1751 :
1752 : /* increase buffer size if truncated */
1753 0 : free(pevent);
1754 0 : buffer_size *= 10;
1755 0 : } while (1);
1756 0 : }
1757 :
1758 0 : void log_odb_dump_json(LOG_CHN * log_chn, short int event_id, INT run_number)
1759 : {
1760 : /* write ODB dump */
1761 :
1762 0 : char* buffer = NULL;
1763 0 : int buffer_size = 0;
1764 0 : int buffer_end = 0;
1765 :
1766 0 : int status = db_copy_json_save(hDB, 0, &buffer, &buffer_size, &buffer_end);
1767 :
1768 : //printf("db_copy_json_save: status %d, buffer_size %d, buffer_end %d\n", status, buffer_size, buffer_end);
1769 :
1770 0 : if (status == DB_SUCCESS) {
1771 0 : int event_size = sizeof(EVENT_HEADER) + buffer_end;
1772 0 : EVENT_HEADER* pevent = (EVENT_HEADER *) malloc(event_size);
1773 0 : if (pevent == NULL) {
1774 0 : cm_msg(MERROR, "log_odb_dump", "Cannot allocate ODB dump buffer size %d", event_size);
1775 : } else {
1776 0 : bm_compose_event(pevent, event_id, MIDAS_MAGIC, buffer_end, run_number);
1777 0 : memcpy(pevent+1, buffer, buffer_end);
1778 0 : log_write(log_chn, pevent);
1779 0 : free(pevent);
1780 : }
1781 : }
1782 0 : free(buffer);
1783 0 : }
1784 :
1785 0 : void log_odb_dump(LOG_CHN * log_chn, short int event_id, INT run_number)
1786 : {
1787 0 : if (equal_ustring(log_chn->settings.odb_dump_format, "odb")) {
1788 0 : log_odb_dump_odb(log_chn, event_id, run_number);
1789 0 : } else if (equal_ustring(log_chn->settings.odb_dump_format, "xml")) {
1790 0 : log_odb_dump_xml(log_chn, event_id, run_number);
1791 0 : } else if (equal_ustring(log_chn->settings.odb_dump_format, "json")) {
1792 0 : log_odb_dump_json(log_chn, event_id, run_number);
1793 : } else {
1794 0 : cm_msg(MERROR, "log_odb_dump", "Invalid ODB dump format \"%s\" in ODB settings for channel \"%s\". Valid formats are: \"odb\", \"xml\", \"json\"", log_chn->settings.odb_dump_format, log_chn->name.c_str());
1795 : }
1796 0 : }
1797 :
1798 : /*---- ODB save routine --------------------------------------------*/
1799 :
1800 0 : void odb_save(const char *filename, bool make_file_readonly)
1801 : {
1802 0 : std::string path;
1803 :
1804 0 : if (strchr(filename, DIR_SEPARATOR) == NULL) {
1805 0 : db_get_value_string(hDB, 0, "/Logger/Data Dir", 0, &path, TRUE, 256);
1806 0 : if (path.length() > 0) {
1807 0 : if (!ends_with_char(path, DIR_SEPARATOR)) {
1808 0 : path += DIR_SEPARATOR_STR;
1809 : }
1810 : }
1811 0 : path += filename;
1812 : } else {
1813 0 : path = filename;
1814 : }
1815 :
1816 : //printf("filename [%s] path [%s]\n", filename, path.c_str());
1817 :
1818 0 : DWORD t0 = ss_millitime();
1819 :
1820 0 : if (ends_with_ustring(filename, ".xml"))
1821 0 : db_save_xml(hDB, 0, path.c_str());
1822 0 : else if (ends_with_ustring(filename, ".json"))
1823 0 : db_save_json(hDB, 0, path.c_str());
1824 : else
1825 0 : db_save(hDB, 0, path.c_str(), FALSE);
1826 :
1827 0 : if (make_file_readonly)
1828 0 : chmod(path.c_str(), 0444);
1829 :
1830 0 : DWORD te = ss_millitime();
1831 :
1832 0 : if (verbose)
1833 0 : printf("saved odb to \"%s\" in %d ms\n", path.c_str(), te-t0);
1834 0 : }
1835 :
1836 :
1837 : #ifdef HAVE_MYSQL
1838 :
1839 0 : static void xwrite(const char* filename, int fd, const void* data, int size)
1840 : {
1841 0 : int wr = write(fd, data, size);
1842 0 : if (wr != size) {
1843 0 : cm_msg(MERROR, "xwrite", "cannot write to \'%s\', write(%d) returned %d, errno %d (%s)", filename, size, wr, errno, strerror(errno));
1844 : }
1845 0 : }
1846 :
1847 : /*==== SQL routines ================================================*/
1848 :
1849 : /*---- Convert ctime() type date/time to SQL 'datetime' ------------*/
1850 :
1851 : typedef struct {
1852 : char column_name[NAME_LENGTH];
1853 : char column_type[NAME_LENGTH];
1854 : char data[256];
1855 : } SQL_LIST;
1856 :
1857 : static const char *mname[] = {
1858 : "January",
1859 : "February",
1860 : "March",
1861 : "April",
1862 : "May",
1863 : "June",
1864 : "July",
1865 : "August",
1866 : "September",
1867 : "October",
1868 : "November",
1869 : "December"
1870 : };
1871 :
1872 0 : void ctime_to_datetime(char *date)
1873 : {
1874 : char ctime_date[30];
1875 : struct tm tms;
1876 : int i;
1877 :
1878 0 : mstrlcpy(ctime_date, date, sizeof(ctime_date));
1879 0 : memset(&tms, 0, sizeof(struct tm));
1880 :
1881 0 : for (i = 0; i < 12; i++)
1882 0 : if (strncmp(ctime_date + 4, mname[i], 3) == 0)
1883 0 : break;
1884 0 : tms.tm_mon = i;
1885 :
1886 0 : tms.tm_mday = atoi(ctime_date + 8);
1887 0 : tms.tm_hour = atoi(ctime_date + 11);
1888 0 : tms.tm_min = atoi(ctime_date + 14);
1889 0 : tms.tm_sec = atoi(ctime_date + 17);
1890 0 : tms.tm_year = atoi(ctime_date + 20) - 1900;
1891 0 : tms.tm_isdst = -1;
1892 :
1893 0 : if (tms.tm_year < 90)
1894 0 : tms.tm_year += 100;
1895 :
1896 0 : ss_mktime(&tms);
1897 0 : sprintf(date, "%d-%02d-%02d %02d-%02d-%02d",
1898 0 : tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec);
1899 0 : }
1900 :
1901 : /*---- mySQL debugging output --------------------------------------*/
1902 :
1903 0 : int mysql_query_debug(MYSQL * db, const char *query)
1904 : {
1905 : int status, fh;
1906 0 : std::string filename;
1907 0 : std::string path;
1908 0 : std::string dir;
1909 : HNDLE hKey;
1910 :
1911 : /* comment in this line if you need debugging output */
1912 : //cm_msg(MINFO, "mysql_query_debug", "SQL query: %s", query);
1913 :
1914 : /* write query into logfile if requested */
1915 0 : filename = "";
1916 0 : db_get_value_string(hDB, 0, "/Logger/Runlog/SQL/Logfile", 0, &filename, TRUE);
1917 0 : if (!filename.empty()) {
1918 0 : status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
1919 0 : if (status == DB_SUCCESS) {
1920 0 : dir = "";
1921 0 : db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &dir, TRUE);
1922 0 : if (!dir.empty())
1923 0 : if (dir.back() != DIR_SEPARATOR)
1924 0 : dir += DIR_SEPARATOR_STR;
1925 :
1926 0 : path = dir + filename;
1927 : } else {
1928 0 : std::string dir = cm_get_path();
1929 0 : if (!dir.empty())
1930 0 : if (dir.back() != DIR_SEPARATOR)
1931 0 : dir += DIR_SEPARATOR_STR;
1932 :
1933 0 : path = dir + filename;
1934 0 : }
1935 :
1936 0 : fh = open(path.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
1937 0 : if (fh < 0) {
1938 0 : printf("Cannot open message log file \'%s\', open() returned %d, errno %d (%s)\n", path.c_str(),
1939 0 : fh, errno, strerror(errno));
1940 : } else {
1941 0 : xwrite(path.c_str(), fh, query, strlen(query));
1942 0 : xwrite(path.c_str(), fh, ";\n", 2);
1943 0 : close(fh);
1944 : }
1945 : }
1946 :
1947 : /* execut sql query */
1948 0 : status = mysql_query(db, query);
1949 :
1950 0 : if (status)
1951 0 : cm_msg(MERROR, "mysql_query_debug", "SQL error: %s", mysql_error(db));
1952 :
1953 0 : return status;
1954 0 : }
1955 :
1956 : /*---- Retrieve list of columns from ODB tree ----------------------*/
1957 :
1958 0 : int sql_get_columns(HNDLE hKeyRoot, SQL_LIST ** sql_list)
1959 : {
1960 : HNDLE hKey;
1961 : int n, i, status;
1962 : KEY key;
1963 :
1964 0 : for (i = 0;; i++) {
1965 0 : status = db_enum_key(hDB, hKeyRoot, i, &hKey);
1966 0 : if (status == DB_NO_MORE_SUBKEYS)
1967 0 : break;
1968 : }
1969 :
1970 0 : if (i == 0)
1971 0 : return 0;
1972 :
1973 0 : n = i;
1974 :
1975 0 : *sql_list = (SQL_LIST *) malloc(sizeof(SQL_LIST) * n);
1976 :
1977 0 : for (i = 0; i < n; i++) {
1978 :
1979 : /* get name of link, NOT of link target */
1980 0 : db_enum_link(hDB, hKeyRoot, i, &hKey);
1981 0 : db_get_link(hDB, hKey, &key);
1982 0 : mstrlcpy((*sql_list)[i].column_name, key.name, NAME_LENGTH);
1983 :
1984 : /* get key */
1985 0 : db_enum_key(hDB, hKeyRoot, i, &hKey);
1986 0 : db_get_key(hDB, hKey, &key);
1987 :
1988 : /* get key data */
1989 0 : int size = key.total_size;
1990 0 : char* data = (char*)malloc(size);
1991 0 : assert(data);
1992 0 : db_get_data(hDB, hKey, data, &size, key.type);
1993 : char str[1000];
1994 0 : std::string s = db_sprintf(data, size, 0, key.type);
1995 0 : mstrlcpy(str, s.c_str(), sizeof(str));
1996 : //printf("AAA key %s size %d %d [%s]\n", key.name, key.total_size, (int)strlen(str), str);
1997 0 : assert(strlen(str) < sizeof(str));
1998 0 : free(data);
1999 0 : if (key.type == TID_BOOL)
2000 0 : strcpy((*sql_list)[i].data, str[0] == 'y' || str[0] == 'Y' ? "1" : "0");
2001 : else
2002 0 : strcpy((*sql_list)[i].data, str);
2003 :
2004 0 : if (key.type == TID_STRING) {
2005 : /* check if string is date/time */
2006 0 : if (strlen(str) == 24 && str[10] == ' ' && str[13] == ':') {
2007 0 : strcpy(str, "DATETIME");
2008 0 : ctime_to_datetime((*sql_list)[i].data);
2009 0 : } else if (key.item_size < 256)
2010 0 : sprintf(str, "VARCHAR (%d)", key.item_size);
2011 : else
2012 0 : sprintf(str, " TEXT");
2013 :
2014 : } else {
2015 0 : switch (key.type) {
2016 0 : case TID_UINT8:
2017 0 : strcpy(str, "TINYINT UNSIGNED ");
2018 0 : break;
2019 0 : case TID_INT8:
2020 0 : strcpy(str, "TINYINT ");
2021 0 : break;
2022 0 : case TID_CHAR:
2023 0 : strcpy(str, "CHAR ");
2024 0 : break;
2025 0 : case TID_UINT16:
2026 0 : strcpy(str, "SMALLINT UNSIGNED ");
2027 0 : break;
2028 0 : case TID_INT16:
2029 0 : strcpy(str, "SMALLINT ");
2030 0 : break;
2031 0 : case TID_UINT32:
2032 0 : strcpy(str, "INT UNSIGNED ");
2033 0 : break;
2034 0 : case TID_INT32:
2035 0 : strcpy(str, "INT ");
2036 0 : break;
2037 0 : case TID_BOOL:
2038 0 : strcpy(str, "BOOLEAN ");
2039 0 : break;
2040 0 : case TID_FLOAT:
2041 0 : strcpy(str, "FLOAT ");
2042 0 : break;
2043 0 : case TID_DOUBLE:
2044 0 : strcpy(str, "DOUBLE ");
2045 0 : break;
2046 0 : default:
2047 0 : cm_msg(MERROR, "sql_create_database",
2048 0 : "No SQL type mapping for key \"%s\" of type %s", key.name, rpc_tid_name(key.type));
2049 : }
2050 : }
2051 :
2052 0 : strcpy((*sql_list)[i].column_type, str);
2053 0 : }
2054 :
2055 0 : return n;
2056 : }
2057 :
2058 : /*---- Create mySQL table from ODB tree ----------------------------*/
2059 :
2060 0 : BOOL sql_create_table(MYSQL * db, char *database, char *table, HNDLE hKeyRoot)
2061 : {
2062 : SQL_LIST *sql_list;
2063 :
2064 0 : std::string query = msprintf("CREATE TABLE `%s`.`%s` (", database, table);
2065 :
2066 0 : int n_col = sql_get_columns(hKeyRoot, &sql_list);
2067 0 : if (n_col == 0) {
2068 0 : std::string path = db_get_path(hDB, hKeyRoot);
2069 0 : cm_msg(MERROR, "sql_create_database", "ODB tree \"%s\" contains no variables", path.c_str());
2070 0 : return FALSE;
2071 0 : }
2072 :
2073 0 : for (int i = 0; i < n_col; i++) {
2074 0 : query += msprintf("`%s` %s NOT NULL, ", sql_list[i].column_name, sql_list[i].column_type);
2075 : }
2076 :
2077 0 : query += msprintf("PRIMARY KEY (`%s`))", sql_list[0].column_name);
2078 0 : free(sql_list);
2079 :
2080 0 : if (mysql_query_debug(db, query.c_str())) {
2081 0 : cm_msg(MERROR, "sql_create_table", "Failed to create table: Error: %s", mysql_error(db));
2082 0 : return FALSE;
2083 : }
2084 :
2085 0 : return TRUE;
2086 0 : }
2087 :
2088 : /*---- Create mySQL table from ODB tree ----------------------------*/
2089 :
2090 0 : BOOL sql_modify_table(MYSQL * db, char *database, char *table, HNDLE hKeyRoot)
2091 : {
2092 : SQL_LIST *sql_list;
2093 :
2094 0 : int n_col = sql_get_columns(hKeyRoot, &sql_list);
2095 0 : if (n_col == 0) {
2096 0 : std::string path = db_get_path(hDB, hKeyRoot);
2097 0 : cm_msg(MERROR, "sql_modify_table", "ODB tree \"%s\" contains no variables", path.c_str());
2098 0 : return FALSE;
2099 0 : }
2100 :
2101 0 : for (int i = 0; i < n_col; i++) {
2102 0 : std::string query;
2103 :
2104 : /* try to add column */
2105 0 : if (i == 0) {
2106 0 : query = msprintf("ALTER TABLE `%s`.`%s` ADD `%s` %s", database, table, sql_list[i].column_name, sql_list[i].column_type);
2107 : } else {
2108 0 : query = msprintf("ALTER TABLE `%s`.`%s` ADD `%s` %s AFTER `%s`",
2109 : database, table,
2110 0 : sql_list[i].column_name,
2111 0 : sql_list[i].column_type,
2112 0 : sql_list[i - 1].column_name);
2113 : }
2114 :
2115 0 : if (mysql_query_debug(db, query.c_str())) {
2116 0 : if (mysql_errno(db) == ER_DUP_FIELDNAME) {
2117 :
2118 : /* try to modify column */
2119 0 : query = msprintf("ALTER TABLE `%s`.`%s` MODIFY `%s` %s", database, table, sql_list[i].column_name, sql_list[i].column_type);
2120 :
2121 0 : if (mysql_query_debug(db, query.c_str())) {
2122 0 : free(sql_list);
2123 0 : cm_msg(MERROR, "sql_modify_table", "Failed to modify column: Error: %s", mysql_error(db));
2124 0 : return FALSE;
2125 : }
2126 :
2127 : } else {
2128 0 : free(sql_list);
2129 0 : cm_msg(MERROR, "sql_modify_table", "Failed to add column: Error: %s", mysql_error(db));
2130 0 : return FALSE;
2131 : }
2132 : }
2133 0 : }
2134 :
2135 0 : cm_msg(MINFO, "sql_insert", "SQL table '%s.%s' modified successfully", database, table);
2136 :
2137 0 : return TRUE;
2138 : }
2139 :
2140 : /*---- Create mySQL database ---------------------------------------*/
2141 :
2142 0 : BOOL sql_create_database(MYSQL * db, const char *database)
2143 : {
2144 0 : std::string query = msprintf("CREATE DATABASE `%s`", database);
2145 0 : if (mysql_query_debug(db, query.c_str())) {
2146 0 : cm_msg(MERROR, "sql_create_database", "Failed to create database: Error: %s", mysql_error(db));
2147 0 : return FALSE;
2148 : }
2149 :
2150 : /* select database */
2151 0 : query = msprintf("USE `%s`", database);
2152 0 : if (mysql_query_debug(db, query.c_str())) {
2153 0 : cm_msg(MERROR, "sql_create_database", "Failed to select database: Error: %s", mysql_error(db));
2154 0 : return FALSE;
2155 : }
2156 :
2157 0 : return TRUE;
2158 0 : }
2159 :
2160 : /*---- Insert table row from ODB tree ------------------------------*/
2161 :
2162 0 : int sql_insert(MYSQL * db, char *database, char *table, HNDLE hKeyRoot, BOOL create_flag)
2163 : {
2164 : SQL_LIST *sql_list;
2165 :
2166 : /*
2167 : build SQL query in the form
2168 : "INSERT INTO `<table>` (`<name>`, <name`,..) VALUES (`<value>`, `value`, ...)
2169 : */
2170 0 : std::string query = msprintf("INSERT INTO `%s`.`%s` (", database, table);
2171 0 : int n_col = sql_get_columns(hKeyRoot, &sql_list);
2172 0 : if (n_col == 0)
2173 0 : return DB_SUCCESS;
2174 :
2175 0 : for (int i = 0; i < n_col; i++) {
2176 0 : query += msprintf("`%s`", sql_list[i].column_name);
2177 0 : if (i < n_col - 1) {
2178 0 : query += ", ";
2179 : }
2180 : }
2181 :
2182 0 : query += ") VALUES (";
2183 :
2184 0 : for (int i = 0; i < n_col; i++) {
2185 0 : query += "'";
2186 :
2187 0 : size_t len = strlen(sql_list[i].data);
2188 0 : char str[len*2+1];
2189 0 : mysql_escape_string(str, sql_list[i].data, len);
2190 0 : query += str;
2191 0 : query += "'";
2192 :
2193 0 : if (i < n_col - 1) {
2194 0 : query += ", ";
2195 : }
2196 0 : }
2197 :
2198 0 : free(sql_list);
2199 0 : sql_list = NULL;
2200 0 : query += ")";
2201 0 : if (mysql_query_debug(db, query.c_str())) {
2202 :
2203 : /* if entry for this run exists alreay return */
2204 0 : if (mysql_errno(db) == ER_DUP_ENTRY) {
2205 :
2206 0 : return ER_DUP_ENTRY;
2207 :
2208 0 : } else if (mysql_errno(db) == ER_NO_SUCH_TABLE && create_flag) {
2209 :
2210 : /* if table does not exist, creat it and try again */
2211 0 : sql_create_table(db, database, table, hKeyRoot);
2212 0 : if (mysql_query_debug(db, query.c_str())) {
2213 0 : cm_msg(MERROR, "sql_insert", "Failed to update database: Error: %s", mysql_error(db));
2214 0 : return mysql_errno(db);
2215 : }
2216 0 : cm_msg(MINFO, "sql_insert", "SQL table '%s.%s' created successfully", database, table);
2217 :
2218 0 : } else if (mysql_errno(db) == ER_BAD_FIELD_ERROR && create_flag) {
2219 :
2220 : /* if table structure is different, adjust it and try again */
2221 0 : sql_modify_table(db, database, table, hKeyRoot);
2222 0 : if (mysql_query_debug(db, query.c_str())) {
2223 0 : cm_msg(MERROR, "sql_insert", "Failed to update database: Error: %s", mysql_error(db));
2224 0 : return mysql_errno(db);
2225 : }
2226 :
2227 : } else {
2228 0 : int status = mysql_errno(db);
2229 0 : cm_msg(MERROR, "sql_insert", "Failed to update database: Errno: %d, Error: %s", status, mysql_error(db));
2230 0 : return mysql_errno(db);
2231 : }
2232 : }
2233 :
2234 0 : return DB_SUCCESS;
2235 0 : }
2236 :
2237 : /*---- Update table row from ODB tree ------------------------------*/
2238 :
2239 0 : int sql_update(MYSQL * db, char *database, char *table, HNDLE hKeyRoot, BOOL create_flag, char *where)
2240 : {
2241 : SQL_LIST *sql_list;
2242 :
2243 : /*
2244 : build SQL query in the form
2245 : "UPDATE `<database`.`<table>` SET `<name>`='<value', ... WHERE `<name>`='value'
2246 : */
2247 :
2248 0 : std::string query = msprintf("UPDATE `%s`.`%s` SET ", database, table);
2249 0 : int n_col = sql_get_columns(hKeyRoot, &sql_list);
2250 0 : if (n_col == 0)
2251 0 : return DB_SUCCESS;
2252 :
2253 0 : for (int i = 0; i < n_col; i++) {
2254 0 : size_t len = strlen(sql_list[i].data);
2255 0 : char str[2*len+1]; // see https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-escape-string.html
2256 0 : mysql_escape_string(str, sql_list[i].data, len);
2257 0 : query += msprintf("`%s`='%s'", sql_list[i].column_name, str);
2258 0 : if (i < n_col - 1) {
2259 0 : query += ", ";
2260 : }
2261 0 : }
2262 0 : free(sql_list);
2263 0 : sql_list = NULL;
2264 :
2265 0 : query += msprintf(" %s", where);
2266 0 : if (mysql_query_debug(db, query.c_str())) {
2267 0 : if (mysql_errno(db) == ER_NO_SUCH_TABLE && create_flag) {
2268 :
2269 : /* if table does not exist, creat it and try again */
2270 0 : sql_create_table(db, database, table, hKeyRoot);
2271 0 : return sql_insert(db, database, table, hKeyRoot, create_flag);
2272 :
2273 0 : } else if (mysql_errno(db) == ER_BAD_FIELD_ERROR && create_flag) {
2274 :
2275 : /* if table structure is different, adjust it and try again */
2276 0 : sql_modify_table(db, database, table, hKeyRoot);
2277 0 : if (mysql_query_debug(db, query.c_str())) {
2278 0 : cm_msg(MERROR, "sql_update", "Failed to update database: Error: %s", mysql_error(db));
2279 0 : return mysql_errno(db);
2280 : }
2281 :
2282 : } else {
2283 0 : cm_msg(MERROR, "sql_update", "Failed to update database: Error: %s", mysql_error(db));
2284 0 : return mysql_errno(db);
2285 : }
2286 : }
2287 :
2288 0 : return DB_SUCCESS;
2289 0 : }
2290 :
2291 : /*---- Create /Logger/Runlog/SQL tree ------------------------------*/
2292 :
2293 0 : void create_runlog_sql_tree()
2294 : {
2295 : char hostname[80], username[80], password[80], table[80], filename[80];
2296 : int size, write_flag, create_flag;
2297 : HNDLE hKeyRoot, hKey;
2298 :
2299 0 : size = sizeof(create_flag);
2300 0 : create_flag = 0;
2301 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Create database", &create_flag, &size, TID_BOOL, TRUE);
2302 :
2303 0 : size = sizeof(write_flag);
2304 0 : write_flag = 0;
2305 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Write data", &write_flag, &size, TID_BOOL, TRUE);
2306 :
2307 0 : size = sizeof(hostname);
2308 0 : strcpy(hostname, "localhost");
2309 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Hostname", hostname, &size, TID_STRING, TRUE);
2310 :
2311 0 : size = sizeof(username);
2312 0 : strcpy(username, "root");
2313 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Username", username, &size, TID_STRING, TRUE);
2314 :
2315 0 : size = sizeof(password);
2316 0 : password[0] = 0;
2317 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Password", password, &size, TID_STRING, TRUE);
2318 :
2319 : /* use experiment name as default database name */
2320 0 : std::string database;
2321 0 : db_get_value_string(hDB, 0, "/Experiment/Name", 0, &database, TRUE);
2322 0 : db_get_value_string(hDB, 0, "/Logger/Runlog/SQL/Database", 0, &database, TRUE);
2323 :
2324 0 : size = sizeof(table);
2325 0 : strcpy(table, "Runlog");
2326 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Table", table, &size, TID_STRING, TRUE);
2327 :
2328 0 : size = sizeof(filename);
2329 0 : strcpy(filename, "sql.log");
2330 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Logfile", filename, &size, TID_STRING, TRUE);
2331 :
2332 0 : db_find_key(hDB, 0, "/Logger/Runlog/SQL/Links BOR", &hKeyRoot);
2333 0 : if (!hKeyRoot) {
2334 : /* create some default links */
2335 0 : db_create_key(hDB, 0, "/Logger/Runlog/SQL/Links BOR", TID_KEY);
2336 :
2337 0 : if (db_find_key(hDB, 0, "/Runinfo/Run number", &hKey) == DB_SUCCESS)
2338 0 : db_create_link(hDB, 0, "/Logger/Runlog/SQL/Links BOR/Run number", "/Runinfo/Run number");
2339 :
2340 0 : if (db_find_key(hDB, 0, "/Experiment/Run parameters/Comment", &hKey) == DB_SUCCESS)
2341 0 : db_create_link(hDB, 0, "/Logger/Runlog/SQL/Links BOR/Comment", "/Experiment/Run parameters/Comment");
2342 :
2343 0 : if (db_find_key(hDB, 0, "/Runinfo/Start time", &hKey) == DB_SUCCESS)
2344 0 : db_create_link(hDB, 0, "/Logger/Runlog/SQL/Links BOR/Start time", "/Runinfo/Start time");
2345 : }
2346 :
2347 0 : db_find_key(hDB, 0, "/Logger/Runlog/SQL/Links EOR", &hKeyRoot);
2348 0 : if (!hKeyRoot) {
2349 : /* create some default links */
2350 0 : db_create_key(hDB, 0, "/Logger/Runlog/SQL/Links EOR", TID_KEY);
2351 :
2352 0 : if (db_find_key(hDB, 0, "/Runinfo/Stop time", &hKey) == DB_SUCCESS)
2353 0 : db_create_link(hDB, 0, "/Logger/Runlog/SQL/Links EOR/Stop time", "/Runinfo/Stop time");
2354 :
2355 0 : if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/Events sent", &hKey) == DB_SUCCESS)
2356 0 : db_create_link(hDB, 0, "/Logger/Runlog/SQL/Links EOR/Number of events",
2357 : "/Equipment/Trigger/Statistics/Events sent");
2358 :
2359 : }
2360 0 : }
2361 :
2362 : /*---- Write ODB tree to SQL table ---------------------------------*/
2363 :
2364 0 : void write_runlog_sql(BOOL bor)
2365 : {
2366 : MYSQL db;
2367 : char hostname[80], username[80], password[80], database[80], table[80], query[5000], where[500];
2368 : int status, size, write_flag, create_flag;
2369 : BOOL insert;
2370 : HNDLE hKey, hKeyRoot;
2371 : SQL_LIST *sql_list;
2372 :
2373 : /* do not update SQL if logger does not write data */
2374 0 : size = sizeof(BOOL);
2375 0 : write_flag = FALSE;
2376 0 : db_get_value(hDB, 0, "/Logger/Write data", &write_flag, &size, TID_BOOL, TRUE);
2377 0 : if (!write_flag)
2378 0 : return;
2379 :
2380 : /* insert SQL on bor, else update */
2381 0 : insert = bor;
2382 :
2383 : /* determine primary key */
2384 0 : db_find_key(hDB, 0, "/Logger/Runlog/SQL/Links BOR", &hKeyRoot);
2385 0 : status = db_enum_link(hDB, hKeyRoot, 0, &hKey);
2386 :
2387 : /* if BOR list empty, take first one from EOR list */
2388 0 : if (status == DB_NO_MORE_SUBKEYS) {
2389 0 : insert = TRUE;
2390 0 : db_find_key(hDB, 0, "/Logger/Runlog/SQL/Links EOR", &hKeyRoot);
2391 0 : status = db_enum_link(hDB, hKeyRoot, 0, &hKey);
2392 0 : if (status == DB_NO_MORE_SUBKEYS)
2393 0 : return;
2394 : }
2395 :
2396 0 : sql_get_columns(hKeyRoot, &sql_list);
2397 0 : sprintf(where, "WHERE `%s`='%s'", sql_list[0].column_name, sql_list[0].data);
2398 0 : free(sql_list);
2399 0 : sql_list = NULL;
2400 :
2401 : /* get BOR or EOR list */
2402 0 : if (bor) {
2403 0 : db_find_key(hDB, 0, "/Logger/Runlog/SQL/Links BOR", &hKeyRoot);
2404 0 : if (!hKeyRoot) {
2405 0 : cm_msg(MERROR, "write_runlog_sql", "Cannot find \"/Logger/Runlog/SQL/Links BOR");
2406 0 : return;
2407 : }
2408 : } else {
2409 0 : db_find_key(hDB, 0, "/Logger/Runlog/SQL/Links EOR", &hKeyRoot);
2410 0 : if (!hKeyRoot) {
2411 0 : cm_msg(MERROR, "write_runlog_sql", "Cannot find \"/Logger/Runlog/SQL/Links EOR");
2412 0 : return;
2413 : }
2414 : }
2415 :
2416 0 : size = sizeof(create_flag);
2417 0 : create_flag = 0;
2418 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Create database", &create_flag, &size, TID_BOOL, TRUE);
2419 :
2420 0 : size = sizeof(write_flag);
2421 0 : write_flag = 0;
2422 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Write data", &write_flag, &size, TID_BOOL, TRUE);
2423 :
2424 0 : size = sizeof(hostname);
2425 0 : strcpy(hostname, "localhost");
2426 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Hostname", hostname, &size, TID_STRING, TRUE);
2427 :
2428 0 : size = sizeof(username);
2429 0 : strcpy(username, "root");
2430 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Username", username, &size, TID_STRING, TRUE);
2431 :
2432 0 : size = sizeof(password);
2433 0 : password[0] = 0;
2434 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Password", password, &size, TID_STRING, TRUE);
2435 :
2436 : /* use experiment name as default database name */
2437 0 : size = sizeof(database);
2438 0 : db_get_value(hDB, 0, "/Experiment/Name", database, &size, TID_STRING, TRUE);
2439 0 : size = sizeof(database);
2440 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Database", database, &size, TID_STRING, TRUE);
2441 :
2442 0 : size = sizeof(table);
2443 0 : strcpy(table, "Runlog");
2444 0 : db_get_value(hDB, 0, "/Logger/Runlog/SQL/Table", table, &size, TID_STRING, TRUE);
2445 :
2446 : /* continue only if data should be written */
2447 0 : if (!write_flag)
2448 0 : return;
2449 :
2450 : /* connect to MySQL database */
2451 0 : mysql_init(&db);
2452 :
2453 0 : if (!mysql_real_connect(&db, hostname, username, password, NULL, 0, NULL, 0)) {
2454 0 : cm_msg(MERROR, "write_runlog_sql", "Failed to connect to database: Error: %s", mysql_error(&db));
2455 0 : mysql_close(&db);
2456 0 : return;
2457 : }
2458 :
2459 : /* select database */
2460 0 : sprintf(query, "USE `%s`", database);
2461 0 : if (mysql_query_debug(&db, query)) {
2462 :
2463 : /* create database if selected */
2464 0 : if (create_flag) {
2465 0 : if (!sql_create_database(&db, database)) {
2466 0 : mysql_close(&db);
2467 0 : return;
2468 : }
2469 0 : cm_msg(MINFO, "write_runlog_sql", "Database \"%s\" created successfully", database);
2470 :
2471 : } else {
2472 0 : cm_msg(MERROR, "write_runlog_sql", "Failed to select database: Error: %s", mysql_error(&db));
2473 0 : mysql_close(&db);
2474 0 : return;
2475 : }
2476 : }
2477 :
2478 0 : if (insert) {
2479 0 : status = sql_insert(&db, database, table, hKeyRoot, create_flag);
2480 0 : if (status == ER_DUP_ENTRY)
2481 0 : sql_update(&db, database, table, hKeyRoot, create_flag, where);
2482 : } else
2483 0 : sql_update(&db, database, table, hKeyRoot, create_flag, where);
2484 :
2485 0 : mysql_close(&db);
2486 : }
2487 :
2488 : #endif // HAVE_MYSQL
2489 :
2490 : /*---- Create /Logger/Runlog/ASCII tree ----------------------------*/
2491 :
2492 0 : void create_runlog_ascii_tree()
2493 : {
2494 0 : std::string filename;
2495 : int size, write_flag;
2496 : HNDLE hKeyRoot, hKey;
2497 :
2498 0 : size = sizeof(write_flag);
2499 0 : write_flag = 0;
2500 0 : db_get_value(hDB, 0, "/Logger/Runlog/ASCII/Write data", &write_flag, &size, TID_BOOL, TRUE);
2501 :
2502 0 : filename = "runlog.log";
2503 0 : db_get_value_string(hDB, 0, "/Logger/Runlog/ASCII/Filename", 0, &filename, TRUE);
2504 :
2505 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links BOR", &hKeyRoot);
2506 0 : if (!hKeyRoot) {
2507 : /* create some default links */
2508 0 : db_create_key(hDB, 0, "/Logger/Runlog/ASCII/Links BOR", TID_KEY);
2509 :
2510 0 : if (db_find_key(hDB, 0, "/Runinfo/Run number", &hKey) == DB_SUCCESS)
2511 0 : db_create_link(hDB, 0, "/Logger/Runlog/ASCII/Links BOR/Run number", "/Runinfo/Run number");
2512 :
2513 0 : if (db_find_key(hDB, 0, "/Experiment/Run parameters/Comment", &hKey) == DB_SUCCESS)
2514 0 : db_create_link(hDB, 0, "/Logger/Runlog/ASCII/Links BOR/Comment", "/Experiment/Run parameters/Comment");
2515 :
2516 0 : if (db_find_key(hDB, 0, "/Runinfo/Start time", &hKey) == DB_SUCCESS)
2517 0 : db_create_link(hDB, 0, "/Logger/Runlog/ASCII/Links BOR/Start time", "/Runinfo/Start time");
2518 : }
2519 :
2520 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links EOR", &hKeyRoot);
2521 0 : if (!hKeyRoot) {
2522 : /* create some default links */
2523 0 : db_create_key(hDB, 0, "/Logger/Runlog/ASCII/Links EOR", TID_KEY);
2524 :
2525 0 : if (db_find_key(hDB, 0, "/Runinfo/Stop time", &hKey) == DB_SUCCESS)
2526 0 : db_create_link(hDB, 0, "/Logger/Runlog/ASCII/Links EOR/Stop time", "/Runinfo/Stop time");
2527 :
2528 0 : if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/Events sent", &hKey) == DB_SUCCESS)
2529 0 : db_create_link(hDB, 0, "/Logger/Runlog/ASCII/Links EOR/Number of events",
2530 : "/Equipment/Trigger/Statistics/Events sent");
2531 :
2532 : }
2533 0 : }
2534 :
2535 : /*---- Write ODB tree to ASCII log file ----------------------------*/
2536 :
2537 0 : void write_runlog_ascii(BOOL bor)
2538 : {
2539 : char filename[256], dir[256], path[256];
2540 : int status, size, write_flag;
2541 : HNDLE hKey, hKeyRoot;
2542 :
2543 : /* do not update runlog if logger does not write data */
2544 0 : size = sizeof(BOOL);
2545 0 : write_flag = FALSE;
2546 0 : db_get_value(hDB, 0, "/Logger/Write data", &write_flag, &size, TID_BOOL, TRUE);
2547 0 : if (!write_flag)
2548 0 : return;
2549 :
2550 : /* get BOR or EOR list */
2551 0 : if (bor) {
2552 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links BOR", &hKeyRoot);
2553 0 : if (!hKeyRoot) {
2554 0 : cm_msg(MERROR, "write_runlog_ascii", "Cannot find \"/Logger/Runlog/ASCII/Links BOR");
2555 0 : return;
2556 : }
2557 : } else {
2558 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links EOR", &hKeyRoot);
2559 0 : if (!hKeyRoot) {
2560 0 : cm_msg(MERROR, "write_runlog_ascii", "Cannot find \"/Logger/Runlog/ASCII/Links EOR");
2561 0 : return;
2562 : }
2563 : }
2564 :
2565 0 : size = sizeof(write_flag);
2566 0 : write_flag = 0;
2567 0 : db_get_value(hDB, 0, "/Logger/Runlog/ASCII/Write data", &write_flag, &size, TID_BOOL, TRUE);
2568 :
2569 0 : size = sizeof(filename);
2570 0 : strcpy(filename, "runlog.log");
2571 0 : db_get_value(hDB, 0, "/Logger/Runlog/ASCII/Filename", filename, &size, TID_STRING, TRUE);
2572 :
2573 0 : if (strchr(filename, DIR_SEPARATOR) == NULL) {
2574 0 : size = sizeof(dir);
2575 0 : dir[0] = 0;
2576 0 : db_get_value(hDB, 0, "/Logger/Message Dir", dir, &size, TID_STRING, TRUE);
2577 0 : if (dir[0] != 0)
2578 0 : if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
2579 0 : strcat(dir, DIR_SEPARATOR_STR);
2580 0 : strcpy(path, dir);
2581 0 : strcat(path, filename);
2582 : } else
2583 0 : strcpy(path, filename);
2584 :
2585 : /* continue only if data should be written */
2586 0 : if (!write_flag)
2587 0 : return;
2588 :
2589 0 : FILE *f = fopen(path, "r");
2590 0 : if (f == NULL) {
2591 : // create new file
2592 0 : f = fopen(path, "wt");
2593 :
2594 0 : assert(f != NULL);
2595 :
2596 : // write column header line with variable names
2597 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links BOR", &hKeyRoot);
2598 0 : for (int i = 0;; i++) {
2599 0 : status = db_enum_key(hDB, hKeyRoot, i, &hKey);
2600 0 : if (status == DB_NO_MORE_SUBKEYS)
2601 0 : break;
2602 : KEY key;
2603 0 : db_get_key(hDB, hKey, &key);
2604 0 : fprintf(f, "%s\t", key.name);
2605 0 : }
2606 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links EOR", &hKeyRoot);
2607 0 : for (int i = 0;; i++) {
2608 0 : status = db_enum_key(hDB, hKeyRoot, i, &hKey);
2609 0 : if (status == DB_NO_MORE_SUBKEYS)
2610 0 : break;
2611 : KEY key;
2612 0 : db_get_key(hDB, hKey, &key);
2613 0 : fprintf(f, "%s\t", key.name);
2614 0 : }
2615 0 : fprintf(f, "\n");
2616 0 : fclose(f);
2617 : }
2618 :
2619 : // append data to logfile
2620 0 : f = fopen(path, "at");
2621 :
2622 0 : assert(f != NULL);
2623 :
2624 0 : if (bor)
2625 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links BOR", &hKeyRoot);
2626 : else
2627 0 : db_find_key(hDB, 0, "/Logger/Runlog/ASCII/Links EOR", &hKeyRoot);
2628 :
2629 0 : for (int i = 0;; i++) {
2630 0 : status = db_enum_key(hDB, hKeyRoot, i, &hKey);
2631 0 : if (status == DB_NO_MORE_SUBKEYS)
2632 0 : break;
2633 : KEY key;
2634 0 : db_get_key(hDB, hKey, &key);
2635 0 : int size = key.total_size;
2636 0 : char* data = (char*)malloc(size);
2637 0 : assert(data);
2638 0 : db_get_data(hDB, hKey, data, &size, key.type);
2639 0 : std::string str = db_sprintf(data, size, 0, key.type);
2640 : //printf("BBB key %s size %d %d [%s]\n", key.name, size, (int)strlen(str), str);
2641 0 : free(data);
2642 0 : fprintf(f, "%s\t", str.c_str());
2643 0 : }
2644 0 : if (!bor)
2645 0 : fprintf(f, "\n");
2646 0 : fclose(f);
2647 : }
2648 :
2649 : /*---- Create /Logger/Runlog/JSON tree ----------------------------*/
2650 :
2651 0 : void create_runlog_json_tree()
2652 : {
2653 : char dirname[256];
2654 : int size, write_flag;
2655 : HNDLE hKeyRoot, hKey;
2656 :
2657 0 : size = sizeof(write_flag);
2658 0 : write_flag = 0;
2659 0 : db_get_value(hDB, 0, "/Logger/Runlog/JSON/Write data", &write_flag, &size, TID_BOOL, TRUE);
2660 :
2661 0 : size = sizeof(dirname);
2662 0 : strcpy(dirname, "runlogs");
2663 0 : db_get_value(hDB, 0, "/Logger/Runlog/JSON/Subdir", dirname, &size, TID_STRING, TRUE);
2664 :
2665 0 : db_find_key(hDB, 0, "/Logger/Runlog/JSON/Links BOR", &hKeyRoot);
2666 0 : if (!hKeyRoot) {
2667 : /* create some default links */
2668 0 : db_create_key(hDB, 0, "/Logger/Runlog/JSON/Links BOR", TID_KEY);
2669 :
2670 0 : if (db_find_key(hDB, 0, "/Runinfo/Run number", &hKey) == DB_SUCCESS)
2671 0 : db_create_link(hDB, 0, "/Logger/Runlog/JSON/Links BOR/Run number", "/Runinfo/Run number");
2672 :
2673 0 : if (db_find_key(hDB, 0, "/Experiment/Run parameters/Comment", &hKey) == DB_SUCCESS)
2674 0 : db_create_link(hDB, 0, "/Logger/Runlog/JSON/Links BOR/Comment", "/Experiment/Run parameters/Comment");
2675 :
2676 0 : if (db_find_key(hDB, 0, "/Runinfo/Start time", &hKey) == DB_SUCCESS)
2677 0 : db_create_link(hDB, 0, "/Logger/Runlog/JSON/Links BOR/Start time", "/Runinfo/Start time");
2678 : }
2679 :
2680 0 : db_find_key(hDB, 0, "/Logger/Runlog/JSON/Links EOR", &hKeyRoot);
2681 0 : if (!hKeyRoot) {
2682 : /* create some default links */
2683 0 : db_create_key(hDB, 0, "/Logger/Runlog/JSON/Links EOR", TID_KEY);
2684 :
2685 0 : if (db_find_key(hDB, 0, "/Runinfo/Stop time", &hKey) == DB_SUCCESS)
2686 0 : db_create_link(hDB, 0, "/Logger/Runlog/JSON/Links EOR/Stop time", "/Runinfo/Stop time");
2687 :
2688 0 : if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/Events sent", &hKey) == DB_SUCCESS)
2689 0 : db_create_link(hDB, 0, "/Logger/Runlog/JSON/Links EOR/Number of events",
2690 : "/Equipment/Trigger/Statistics/Events sent");
2691 :
2692 : }
2693 0 : }
2694 :
2695 :
2696 : /*---- Write ODB tree to JSON log file (one per run) -----------------*/
2697 :
2698 0 : void write_runlog_json(BOOL bor) {
2699 : char filename[256], messagedir[256], datadir[256], dirname[256], path[256];
2700 : int status, size, write_flag;
2701 : int runnumber;
2702 : HNDLE hKey;
2703 :
2704 : /* do not update runlog if logger does not write data */
2705 0 : size = sizeof(BOOL);
2706 0 : write_flag = FALSE;
2707 0 : db_get_value(hDB, 0, "/Logger/Write data", &write_flag, &size, TID_BOOL, TRUE);
2708 0 : if (!write_flag)
2709 0 : return;
2710 :
2711 : /* get BOR or EOR list */
2712 0 : if (bor) {
2713 0 : db_find_key(hDB, 0, "/Logger/Runlog/JSON/Links BOR", &hKey);
2714 0 : if (!hKey) {
2715 0 : cm_msg(MERROR, "write_runlog_json", "Cannot find \"/Logger/Runlog/JSON/Links BOR");
2716 0 : return;
2717 : }
2718 : } else {
2719 0 : db_find_key(hDB, 0, "/Logger/Runlog/JSON/Links EOR", &hKey);
2720 0 : if (!hKey) {
2721 0 : cm_msg(MERROR, "write_runlog_json", "Cannot find \"/Logger/Runlog/JSON/Links EOR");
2722 0 : return;
2723 : }
2724 : }
2725 :
2726 0 : size = sizeof(write_flag);
2727 0 : write_flag = 0;
2728 0 : db_get_value(hDB, 0, "/Logger/Runlog/JSON/Write data", &write_flag, &size, TID_BOOL, TRUE);
2729 :
2730 0 : size = sizeof(datadir);
2731 0 : strcpy(datadir, "");
2732 0 : db_get_value(hDB, 0, "/Logger/Data Dir", datadir, &size, TID_STRING, TRUE);
2733 :
2734 0 : size = sizeof(messagedir);
2735 0 : strcpy(messagedir, "");
2736 0 : db_get_value(hDB, 0, "/Logger/Message Dir", messagedir, &size, TID_STRING, TRUE);
2737 :
2738 0 : size = sizeof(dirname);
2739 0 : strcpy(dirname, "runlogs");
2740 0 : db_get_value(hDB, 0, "/Logger/Runlog/JSON/Subdir", dirname, &size, TID_STRING, TRUE);
2741 :
2742 0 : size = sizeof(runnumber);
2743 0 : db_get_value(hDB, 0, "/Runinfo/Run number", &runnumber, &size, TID_INT32, FALSE);
2744 :
2745 0 : snprintf(filename, 256, "runlog_%06i.json", runnumber);
2746 :
2747 : // use /Logger/Message dir, and if empty use /Logger/Data dir
2748 0 : mstrlcpy(path, messagedir, sizeof(path));
2749 0 : if (path[0] == 0)
2750 0 : mstrlcpy(path, datadir, sizeof(path));
2751 0 : if (path[strlen(path) - 1] != DIR_SEPARATOR)
2752 0 : mstrlcat(path, DIR_SEPARATOR_STR, sizeof(path));
2753 0 : mstrlcat(path, dirname, sizeof(path));
2754 0 : if (path[strlen(path) - 1] != DIR_SEPARATOR)
2755 0 : mstrlcat(path, DIR_SEPARATOR_STR, sizeof(path));
2756 :
2757 : /* create directory if needed */
2758 : #ifdef OS_WINNT
2759 : status = mkdir(path);
2760 : #else
2761 0 : status = mkdir(path, 0755);
2762 : #endif
2763 :
2764 0 : mstrlcat(path, filename, sizeof(path));
2765 :
2766 : /* continue only if data should be written */
2767 0 : if (!write_flag)
2768 0 : return;
2769 :
2770 0 : char fileflag[2] = "a";
2771 0 : if (bor)
2772 0 : strcpy(fileflag, "a");
2773 :
2774 0 : FILE *file = fopen(path, fileflag);
2775 0 : if (file == NULL) {
2776 0 : cm_msg(MERROR, "write_runlog_json", "Cannot open file \"%s\"", path);
2777 0 : return;
2778 : }
2779 :
2780 0 : if (bor)
2781 0 : db_find_key(hDB, 0, "/Logger/Runlog/JSON/Links BOR", &hKey);
2782 : else
2783 0 : db_find_key(hDB, 0, "/Logger/Runlog/JSON/Links EOR", &hKey);
2784 :
2785 0 : int buffer_size = 100000;
2786 0 : char *buffer = (char *)malloc(buffer_size);
2787 0 : int buffer_end = 0;
2788 :
2789 0 : if (bor)
2790 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, "{\n \"BOR\": ", 0);
2791 : else
2792 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, " \"EOR\": ", 0);
2793 :
2794 0 : if (!rpc_is_remote())
2795 0 : db_lock_database(hDB);
2796 0 : int flags = JSFLAG_FOLLOW_LINKS|JSFLAG_RECURSE|JSFLAG_OMIT_NAMES|JSFLAG_OMIT_LAST_WRITTEN|JSFLAG_OMIT_OLD;
2797 0 : status = json_write_anything(hDB, hKey, &buffer, &buffer_size, &buffer_end, JS_LEVEL_1, 0, flags, 0);
2798 0 : if (!rpc_is_remote())
2799 0 : db_unlock_database(hDB);
2800 :
2801 0 : if (bor)
2802 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
2803 : else
2804 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, " \n}\n", 0);
2805 :
2806 0 : if (status == DB_SUCCESS) {
2807 0 : if (buffer) {
2808 0 : size_t wr = fwrite(buffer, 1, buffer_end, file);
2809 0 : if (wr != (size_t) buffer_end) {
2810 0 : cm_msg(MERROR, "write_runlog_json", "Cannot write to file \"%s\", fwrite() errno %d (%s)", filename, errno, strerror(errno));
2811 0 : free(buffer);
2812 0 : fclose(file);
2813 0 : return;
2814 : }
2815 : }
2816 : }
2817 :
2818 0 : if (buffer)
2819 0 : free(buffer);
2820 :
2821 0 : fclose(file);
2822 : }
2823 :
2824 : /*---- open FTP channel --------------------------------------------*/
2825 :
2826 0 : INT ftp_error(const char *message)
2827 : {
2828 0 : cm_msg(MERROR, "ftp_error", "%s", message);
2829 0 : return 1;
2830 : }
2831 :
2832 0 : INT ftp_open(const char *xdestination, FTP_CON ** con)
2833 : {
2834 : INT status;
2835 0 : short port = 0;
2836 : char *token;
2837 : char host_name[HOST_NAME_LENGTH];
2838 : char user[32], pass[32];
2839 : char directory[256], file_name[256], file_mode[256];
2840 : char bdestination[256]; // have to make a copy of destination because strtok() modifies it's string
2841 0 : mstrlcpy(bdestination, xdestination, sizeof(bdestination));
2842 0 : char* destination = bdestination;
2843 :
2844 : // skip leading slash
2845 0 : if (destination[0] == '/')
2846 0 : destination += 1;
2847 :
2848 : /*
2849 : destination should have the form:
2850 : host, port, user, password, directory, run%05d.mid
2851 : */
2852 :
2853 : /* break destination in components */
2854 0 : token = strtok(destination, ",");
2855 0 : if (token)
2856 0 : mstrlcpy(host_name, token, sizeof(host_name));
2857 :
2858 0 : token = strtok(NULL, ", ");
2859 0 : if (token)
2860 0 : port = atoi(token);
2861 :
2862 0 : token = strtok(NULL, ", ");
2863 0 : if (token)
2864 0 : mstrlcpy(user, token, sizeof(user));
2865 :
2866 0 : token = strtok(NULL, ", ");
2867 0 : if (token)
2868 0 : mstrlcpy(pass, token, sizeof(pass));
2869 :
2870 0 : token = strtok(NULL, ", ");
2871 0 : if (token)
2872 0 : mstrlcpy(directory, token, sizeof(directory));
2873 :
2874 0 : token = strtok(NULL, ", ");
2875 0 : if (token)
2876 0 : mstrlcpy(file_name, token, sizeof(file_name));
2877 :
2878 0 : token = strtok(NULL, ", ");
2879 0 : file_mode[0] = 0;
2880 0 : if (token)
2881 0 : mstrlcpy(file_mode, token, sizeof(file_mode));
2882 :
2883 : #ifdef FAL_MAIN
2884 : ftp_debug(NULL, ftp_error);
2885 : #else
2886 0 : ftp_debug((int (*)(const char *)) puts, ftp_error);
2887 : #endif
2888 :
2889 0 : status = ftp_login(con, host_name, port, user, pass, "");
2890 0 : if (status >= 0)
2891 0 : return status;
2892 :
2893 0 : status = ftp_chdir(*con, directory);
2894 0 : if (status >= 0)
2895 0 : return status;
2896 :
2897 0 : status = ftp_binary(*con);
2898 0 : if (status >= 0)
2899 0 : return status;
2900 :
2901 0 : if (file_mode[0]) {
2902 0 : status = ftp_command(*con, "umask %s", file_mode, 200, 250, EOF);
2903 0 : if (status >= 0)
2904 0 : return status;
2905 : }
2906 :
2907 0 : if (ftp_open_write(*con, file_name) >= 0)
2908 0 : return (*con)->err_no;
2909 :
2910 0 : return SS_SUCCESS;
2911 : }
2912 :
2913 : /*---- FTP writer --------------------------------------------------*/
2914 :
2915 : class WriterFtp : public WriterInterface
2916 : {
2917 : public:
2918 0 : WriterFtp(LOG_CHN* log_chn) // ctor
2919 0 : {
2920 0 : if (fTrace)
2921 0 : printf("WriterFtp: path [%s]\n", log_chn->path.c_str());
2922 :
2923 0 : fFtp = NULL;
2924 0 : }
2925 :
2926 0 : ~WriterFtp() // dtor
2927 0 : {
2928 0 : if (fTrace)
2929 0 : printf("WriterFtp: destructor\n");
2930 :
2931 0 : if (fFtp) {
2932 0 : ftp_bye(fFtp);
2933 0 : fFtp = NULL;
2934 : }
2935 0 : }
2936 :
2937 0 : int wr_open(LOG_CHN* log_chn, int run_number)
2938 : {
2939 0 : fBytesIn = 0;
2940 0 : fBytesOut = 0;
2941 :
2942 0 : if (fTrace)
2943 0 : printf("WriterFtp: open path [%s]\n", log_chn->path.c_str());
2944 :
2945 0 : assert(fFtp == NULL);
2946 :
2947 0 : int status = ftp_open(log_chn->path.c_str(), &fFtp);
2948 0 : if (status != SS_SUCCESS || fFtp == NULL) {
2949 0 : cm_msg(MERROR, "WriterFtp::wr_open", "Cannot open FTP connection \'%s\', ftp_open() status %d, errno %d (%s)", log_chn->path.c_str(), status, errno, strerror(errno));
2950 0 : return SS_FILE_ERROR;
2951 : }
2952 :
2953 0 : log_chn->handle = 9999;
2954 :
2955 0 : return SUCCESS;
2956 : }
2957 :
2958 0 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
2959 : {
2960 0 : if (fTrace)
2961 0 : printf("WriterFtp: write path [%s], size %d\n", log_chn->path.c_str(), size);
2962 :
2963 0 : if (size == 0)
2964 0 : return SUCCESS;
2965 :
2966 0 : if (fFtp == NULL) {
2967 0 : return SS_FILE_ERROR;
2968 : }
2969 :
2970 0 : fBytesIn += size;
2971 :
2972 0 : int wr = ftp_send(fFtp->data, (const char*)data, size);
2973 :
2974 0 : if (wr > 0)
2975 0 : fBytesOut += wr;
2976 :
2977 0 : if (wr != size) {
2978 0 : cm_msg(MERROR, "WriterFtp::wr_write", "Cannot write to FTP connection \'%s\', ftp_send(%d) returned %d, errno %d (%s)", log_chn->path.c_str(), size, wr, errno, strerror(errno));
2979 0 : return SS_FILE_ERROR;
2980 : }
2981 :
2982 0 : return SUCCESS;
2983 : }
2984 :
2985 0 : int wr_close(LOG_CHN* log_chn, int run_number)
2986 : {
2987 0 : if (fTrace)
2988 0 : printf("WriterFtp: close path [%s]\n", log_chn->path.c_str());
2989 :
2990 0 : assert(fFtp != NULL);
2991 :
2992 0 : ftp_close(fFtp);
2993 0 : ftp_bye(fFtp);
2994 0 : fFtp = NULL;
2995 :
2996 0 : log_chn->handle = 0;
2997 :
2998 0 : return SUCCESS;
2999 : }
3000 :
3001 0 : std::string wr_get_file_ext()
3002 : {
3003 0 : return "";
3004 : }
3005 :
3006 0 : std::string wr_get_chain()
3007 : {
3008 0 : return "FTP";
3009 : }
3010 :
3011 : private:
3012 : FTP_CON* fFtp;
3013 : };
3014 :
3015 : /*---- MIDAS format routines ---------------------------------------*/
3016 :
3017 : #ifdef OBSOLETE
3018 : INT midas_flush_buffer(LOG_CHN * log_chn)
3019 : {
3020 : INT size, written = 0;
3021 : off_t n;
3022 :
3023 : MIDAS_INFO* info = log_chn->midas_info;
3024 : size = (POINTER_T) info->write_pointer - (POINTER_T) info->buffer;
3025 :
3026 : if (size == 0)
3027 : return 0;
3028 :
3029 : /* write record to device */
3030 : if (log_chn->type == LOG_TYPE_FTP)
3031 : written =
3032 : ftp_send(((FTP_CON *) log_chn->ftp_con)->data, info->buffer,
3033 : size) == size ? SS_SUCCESS : SS_FILE_ERROR;
3034 : else if (log_chn->gzfile) {
3035 : n = lseek(log_chn->handle, 0, SEEK_CUR);
3036 : if (gzwrite((gzFile) log_chn->gzfile, info->buffer, size) != size)
3037 : return -1;
3038 : written = lseek(log_chn->handle, 0, SEEK_CUR) - n;
3039 : } else if (log_chn->pfile) {
3040 : written = fwrite(info->buffer, size, 1,log_chn->pfile);
3041 : if (errno == EPIPE){
3042 : cm_msg(MERROR, "midas_flush_buffer",
3043 : "pipe was broken for redirection of data stream, please check the command usage:\"%s\"\nYou can try to run mlogger in console in interactive mode(just \"mlogger\") to obtaine more detail error message",
3044 : log_chn->pipe_command.c_str());
3045 : log_chn->handle=0;
3046 : }
3047 : if (written !=1 )
3048 : return -1;
3049 : written = size;
3050 : } else if (log_chn->handle) {
3051 : #ifdef OS_WINNT
3052 : WriteFile((HANDLE) log_chn->handle, info->buffer, size, (unsigned long *) &written, NULL);
3053 : #else
3054 : written = write(log_chn->handle, info->buffer, size);
3055 : #endif
3056 : } else {
3057 : /* we are writing into the void!?! */
3058 : written = 0;
3059 : }
3060 :
3061 : info->write_pointer = info->buffer;
3062 :
3063 : return written;
3064 : }
3065 : #endif
3066 :
3067 : /*------------------------------------------------------------------*/
3068 :
3069 : #ifdef OBSOLETE
3070 : INT midas_write(LOG_CHN * log_chn, EVENT_HEADER * pevent, INT evt_size)
3071 : {
3072 : INT i, written, size_left;
3073 :
3074 : MIDAS_INFO* info = log_chn->midas_info;
3075 : written = 0;
3076 :
3077 : /* check if event fits into buffer */
3078 : size_left = TAPE_BUFFER_SIZE - ((POINTER_T) info->write_pointer - (POINTER_T) info->buffer);
3079 :
3080 : if (size_left < evt_size) {
3081 : /* copy first part of event */
3082 : memcpy(info->write_pointer, pevent, size_left);
3083 : info->write_pointer += size_left;
3084 :
3085 : /* flush buffer */
3086 : written += midas_flush_buffer(log_chn);
3087 : if (written < 0)
3088 : return -1;
3089 :
3090 : /* several writes for large events */
3091 : while (evt_size - size_left >= TAPE_BUFFER_SIZE) {
3092 : memcpy(info->buffer, (char *) pevent + size_left, TAPE_BUFFER_SIZE);
3093 : info->write_pointer += TAPE_BUFFER_SIZE;
3094 : size_left += TAPE_BUFFER_SIZE;
3095 :
3096 : i = midas_flush_buffer(log_chn);
3097 : if (i < 0)
3098 : return -1;
3099 :
3100 : written += i;
3101 : }
3102 :
3103 : /* copy remaining part of event */
3104 : memcpy(info->buffer, (char *) pevent + size_left, evt_size - size_left);
3105 : info->write_pointer = info->buffer + (evt_size - size_left);
3106 : } else {
3107 : /* copy event to buffer */
3108 : memcpy(info->write_pointer, pevent, evt_size);
3109 : info->write_pointer += evt_size;
3110 : }
3111 :
3112 : /* update statistics */
3113 : return written;
3114 : }
3115 : #endif
3116 :
3117 : /*------------------------------------------------------------------*/
3118 :
3119 : #ifdef OBSOLETE
3120 : INT midas_log_open(LOG_CHN * log_chn, INT run_number)
3121 : {
3122 : INT status;
3123 :
3124 : /* allocate MIDAS buffer info */
3125 : log_chn->midas_info = (MIDAS_INFO*) malloc(sizeof(MIDAS_INFO));
3126 :
3127 : MIDAS_INFO* info = log_chn->midas_info;
3128 : if (info == NULL) {
3129 : log_chn->handle = 0;
3130 : return SS_NO_MEMORY;
3131 : }
3132 :
3133 : /* allocate full ring buffer for that channel */
3134 : if ((info->buffer = (char *) malloc(TAPE_BUFFER_SIZE)) == NULL) {
3135 : free(info);
3136 : log_chn->handle = 0;
3137 : return SS_NO_MEMORY;
3138 : }
3139 :
3140 : info->write_pointer = info->buffer;
3141 :
3142 : /* Create device channel */
3143 : if (log_chn->type == LOG_TYPE_FTP) {
3144 : status = ftp_open(log_chn->path.c_str(), &log_chn->ftp_con);
3145 : if (status != SS_SUCCESS) {
3146 : free(info->buffer);
3147 : free(info);
3148 : log_chn->handle = 0;
3149 : return status;
3150 : } else {
3151 : log_chn->handle = 1;
3152 : log_chn->do_disk_level = FALSE;
3153 : log_chn->statistics.disk_level = -1;
3154 : }
3155 : } else {
3156 : /* check if file exists */
3157 : if (strstr(log_chn->path.c_str(), "null") == NULL) {
3158 : log_chn->handle = open(log_chn->path.c_str(), O_RDONLY);
3159 : if (log_chn->handle > 0) {
3160 : /* check if file length is nonzero */
3161 : if (lseek(log_chn->handle, 0, SEEK_END) > 0) {
3162 : close(log_chn->handle);
3163 : free(info->buffer);
3164 : free(info);
3165 : log_chn->handle = 0;
3166 : return SS_FILE_EXISTS;
3167 : }
3168 : }
3169 : }
3170 :
3171 : log_chn->gzfile = NULL;
3172 : log_chn->pfile = NULL;
3173 : log_chn->handle = 0;
3174 :
3175 : /* check that compression level and file name match each other */
3176 : if (1) {
3177 : const char *sufp = strstr(log_chn->path.c_str(), ".gz");
3178 : int isgz = sufp && sufp[3]==0;
3179 :
3180 : if (log_chn->compression>0 && !isgz) {
3181 : cm_msg(MERROR, "midas_log_open", "Compression level %d enabled, but output file name \'%s\' does not end with '.gz'", log_chn->compression, log_chn->path.c_str());
3182 : free(info->buffer);
3183 : free(info);
3184 : return SS_FILE_ERROR;
3185 : }
3186 :
3187 : if (log_chn->compression==0 && isgz && log_chn->pipe_command[0] == 0) {
3188 : cm_msg(MERROR, "midas_log_open",
3189 : "Output file name ends with '.gz', but compression level is zero");
3190 : free(info->buffer);
3191 : free(info);
3192 : return SS_FILE_ERROR;
3193 : }
3194 : }
3195 :
3196 : if (log_chn->pipe_command[0] != 0){
3197 : #ifdef OS_WINNT
3198 : cm_msg(MERROR, "midas_log_open", "Error: Pipe command not supported under Widnows");
3199 : return SS_FILE_ERROR;
3200 : #else
3201 : log_chn->pfile = popen(log_chn->pipe_command.c_str(), "w");
3202 : log_chn->handle = 1;
3203 : if (log_chn->pfile == NULL) {
3204 : cm_msg(MERROR, "midas_log_open", "Error: popen() failed, cannot open pipe stream");
3205 : free(info->buffer);
3206 : free(info);
3207 : log_chn->handle = 0;
3208 : return SS_FILE_ERROR;
3209 : }
3210 : #endif
3211 : } else {
3212 : #ifdef OS_WINNT
3213 : log_chn->handle = (int) CreateFile(log_chn->path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL,
3214 : CREATE_ALWAYS,
3215 : FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_SEQUENTIAL_SCAN, 0);
3216 : #else
3217 : log_chn->handle = open(log_chn->path.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_BINARY | O_LARGEFILE, 0444);
3218 : #endif
3219 : if (log_chn->handle < 0) {
3220 : free(info->buffer);
3221 : free(info);
3222 : log_chn->handle = 0;
3223 : return SS_FILE_ERROR;
3224 : }
3225 :
3226 : log_chn->do_disk_level = TRUE;
3227 :
3228 : if (log_chn->compression > 0) {
3229 : log_chn->gzfile = gzdopen(log_chn->handle, "wb");
3230 : if (log_chn->gzfile == NULL) {
3231 : cm_msg(MERROR, "midas_log_open", "Error: gzdopen() failed, cannot open compression stream");
3232 : free(info->buffer);
3233 : free(info);
3234 : log_chn->handle = 0;
3235 : return SS_FILE_ERROR;
3236 : }
3237 :
3238 : gzsetparams((gzFile)log_chn->gzfile, log_chn->compression, Z_DEFAULT_STRATEGY);
3239 : }
3240 : }
3241 : }
3242 :
3243 : /* write ODB dump */
3244 : if (log_chn->settings.odb_dump)
3245 : log_odb_dump(log_chn, EVENTID_BOR, run_number);
3246 :
3247 : return SS_SUCCESS;
3248 : }
3249 : #endif
3250 :
3251 : /*------------------------------------------------------------------*/
3252 :
3253 : #ifdef OBSOLETE
3254 : INT midas_log_close(LOG_CHN * log_chn, INT run_number)
3255 : {
3256 : int written;
3257 : off_t n;
3258 :
3259 : /* write ODB dump */
3260 : if (log_chn->settings.odb_dump)
3261 : log_odb_dump(log_chn, EVENTID_EOR, run_number);
3262 :
3263 : written = midas_flush_buffer(log_chn);
3264 :
3265 : /* update statistics */
3266 : log_chn->statistics.bytes_written += written;
3267 : log_chn->statistics.bytes_written_total += written;
3268 :
3269 : if (log_chn->type == LOG_TYPE_FTP) {
3270 : ftp_close(log_chn->ftp_con);
3271 : ftp_bye(log_chn->ftp_con);
3272 : } else {
3273 : if (log_chn->gzfile) {
3274 : n = lseek(log_chn->handle, 0, SEEK_CUR);
3275 : gzflush((gzFile) log_chn->gzfile, Z_FULL_FLUSH);
3276 : written = lseek(log_chn->handle, 0, SEEK_CUR) - n;
3277 : written += 10; // trailer? Obtained experimentally
3278 : gzclose((gzFile) log_chn->gzfile);
3279 : log_chn->statistics.bytes_written += written;
3280 : log_chn->statistics.bytes_written_total += written;
3281 : log_chn->gzfile = NULL;
3282 : }
3283 : #ifdef OS_WINNT
3284 : CloseHandle((HANDLE) log_chn->handle);
3285 : #else
3286 : if (log_chn->pfile) {
3287 : pclose(log_chn->pfile);
3288 : log_chn->pfile = NULL;
3289 : } else {
3290 : close(log_chn->handle);
3291 : }
3292 : #endif
3293 : log_chn->handle = 0;
3294 : }
3295 :
3296 : assert(log_chn->midas_info != NULL);
3297 : assert(log_chn->midas_info->buffer != NULL);
3298 :
3299 : free(log_chn->midas_info->buffer);
3300 : log_chn->midas_info->buffer = NULL;
3301 : free(log_chn->midas_info);
3302 : log_chn->midas_info = NULL;
3303 :
3304 : return SS_SUCCESS;
3305 : }
3306 : #endif
3307 :
3308 : /*-- db_get_event_definition ---------------------------------------*/
3309 :
3310 : typedef struct {
3311 : short int event_id;
3312 : WORD format;
3313 : HNDLE hDefKey;
3314 : } EVENT_DEF;
3315 :
3316 0 : EVENT_DEF *db_get_event_definition(short int event_id)
3317 : {
3318 : INT i, index, status, size;
3319 : char str[80];
3320 : HNDLE hKey, hKeyRoot;
3321 : WORD id;
3322 :
3323 : #define EVENT_DEF_CACHE_SIZE 30
3324 : static EVENT_DEF *event_def = NULL;
3325 :
3326 : /* allocate memory for cache */
3327 0 : if (event_def == NULL)
3328 0 : event_def = (EVENT_DEF *) calloc(EVENT_DEF_CACHE_SIZE, sizeof(EVENT_DEF));
3329 :
3330 0 : assert(event_def != NULL);
3331 :
3332 : /* lookup if event definition in cache */
3333 0 : for (i = 0; event_def[i].event_id; i++)
3334 0 : if (event_def[i].event_id == event_id)
3335 0 : return &event_def[i];
3336 :
3337 : /* search free cache entry */
3338 0 : for (index = 0; index < EVENT_DEF_CACHE_SIZE; index++)
3339 0 : if (event_def[index].event_id == 0)
3340 0 : break;
3341 :
3342 0 : if (index == EVENT_DEF_CACHE_SIZE) {
3343 0 : cm_msg(MERROR, "db_get_event_definition", "too many event definitions");
3344 0 : return NULL;
3345 : }
3346 :
3347 : /* check for system events */
3348 0 : if (event_id < 0) {
3349 0 : event_def[index].event_id = event_id;
3350 0 : event_def[index].format = FORMAT_MIDAS;
3351 0 : event_def[index].hDefKey = 0;
3352 0 : return &event_def[index];
3353 : }
3354 :
3355 0 : status = db_find_key(hDB, 0, "/equipment", &hKeyRoot);
3356 0 : if (status != DB_SUCCESS) {
3357 0 : cm_msg(MERROR, "db_get_event_definition", "cannot find /equipment entry in ODB");
3358 0 : return NULL;
3359 : }
3360 :
3361 0 : for (i = 0;; i++) {
3362 : /* search for client with specific name */
3363 0 : status = db_enum_key(hDB, hKeyRoot, i, &hKey);
3364 0 : if (status == DB_NO_MORE_SUBKEYS) {
3365 0 : cm_msg(MERROR, "db_get_event_definition", "Cannot find event id %d under /equipment", event_id);
3366 0 : return NULL;
3367 : }
3368 :
3369 0 : size = sizeof(id);
3370 0 : status = db_get_value(hDB, hKey, "Common/Event ID", &id, &size, TID_UINT16, TRUE);
3371 0 : if (status != DB_SUCCESS)
3372 0 : continue;
3373 :
3374 0 : if (id == event_id) {
3375 : /* set cache entry */
3376 0 : event_def[index].event_id = id;
3377 :
3378 0 : size = sizeof(str);
3379 0 : str[0] = 0;
3380 0 : db_get_value(hDB, hKey, "Common/Format", str, &size, TID_STRING, TRUE);
3381 :
3382 0 : if (equal_ustring(str, "Fixed"))
3383 0 : event_def[index].format = FORMAT_FIXED;
3384 0 : else if (equal_ustring(str, "MIDAS"))
3385 0 : event_def[index].format = FORMAT_MIDAS;
3386 : else {
3387 0 : cm_msg(MERROR, "db_get_event_definition", "unknown data format name \"%s\"", str);
3388 0 : event_def[index].event_id = 0;
3389 0 : return NULL;
3390 : }
3391 :
3392 0 : db_find_key(hDB, hKey, "Variables", &event_def[index].hDefKey);
3393 0 : return &event_def[index];
3394 : }
3395 : }
3396 : }
3397 :
3398 : /*---- ROOT format routines ----------------------------------------*/
3399 :
3400 : #ifdef HAVE_ROOT
3401 :
3402 : #define MAX_BANKS 100
3403 :
3404 : typedef struct {
3405 : int event_id;
3406 : TTree *tree;
3407 : int n_branch;
3408 : DWORD branch_name[MAX_BANKS];
3409 : int branch_filled[MAX_BANKS];
3410 : int branch_len[MAX_BANKS];
3411 : TBranch *branch[MAX_BANKS];
3412 : } EVENT_TREE;
3413 :
3414 : struct TREE_STRUCT {
3415 : TFile *f;
3416 : int n_tree;
3417 : EVENT_TREE *event_tree;
3418 : };
3419 :
3420 : /*------------------------------------------------------------------*/
3421 :
3422 : INT root_book_trees(TREE_STRUCT * tree_struct)
3423 : {
3424 : int index, size, status;
3425 : WORD id;
3426 : char str[1000];
3427 : HNDLE hKeyRoot, hKeyEq;
3428 : KEY eqkey;
3429 : EVENT_TREE *et;
3430 :
3431 : status = db_find_key(hDB, 0, "/Equipment", &hKeyRoot);
3432 : if (status != DB_SUCCESS) {
3433 : cm_msg(MERROR, "root_book_trees", "cannot find \"/Equipment\" entry in ODB");
3434 : return 0;
3435 : }
3436 :
3437 : tree_struct->n_tree = 0;
3438 :
3439 : for (index = 0;; index++) {
3440 : /* loop through all events under /Equipment */
3441 : status = db_enum_key(hDB, hKeyRoot, index, &hKeyEq);
3442 : if (status == DB_NO_MORE_SUBKEYS)
3443 : return 1;
3444 :
3445 : db_get_key(hDB, hKeyEq, &eqkey);
3446 :
3447 : /* create tree */
3448 : tree_struct->n_tree++;
3449 : if (tree_struct->n_tree == 1)
3450 : tree_struct->event_tree = (EVENT_TREE *) malloc(sizeof(EVENT_TREE));
3451 : else
3452 : tree_struct->event_tree =
3453 : (EVENT_TREE *) realloc(tree_struct->event_tree, sizeof(EVENT_TREE) * tree_struct->n_tree);
3454 :
3455 : assert(tree_struct->event_tree != NULL);
3456 :
3457 : et = tree_struct->event_tree + (tree_struct->n_tree - 1);
3458 :
3459 : size = sizeof(id);
3460 : status = db_get_value(hDB, hKeyEq, "Common/Event ID", &id, &size, TID_UINT16, TRUE);
3461 : if (status != DB_SUCCESS)
3462 : continue;
3463 :
3464 : et->event_id = id;
3465 : et->n_branch = 0;
3466 :
3467 : /* check format */
3468 : size = sizeof(str);
3469 : str[0] = 0;
3470 : db_get_value(hDB, hKeyEq, "Common/Format", str, &size, TID_STRING, TRUE);
3471 :
3472 : if (!equal_ustring(str, "MIDAS")) {
3473 : cm_msg(MERROR, "root_book_events",
3474 : "ROOT output only for MIDAS events, but %s in %s format", eqkey.name, str);
3475 : return 0;
3476 : }
3477 :
3478 : /* create tree */
3479 : sprintf(str, "Event \"%s\", ID %d", eqkey.name, id);
3480 : et->tree = new TTree(eqkey.name, str);
3481 : }
3482 :
3483 : return 1;
3484 : }
3485 :
3486 : /*------------------------------------------------------------------*/
3487 :
3488 : INT root_book_bank(EVENT_TREE * et, HNDLE hKeyDef, int event_id, char *bank_name)
3489 : {
3490 : int i, status;
3491 : char str[1000];
3492 : HNDLE hKeyVar, hKeySubVar;
3493 : KEY varkey, subvarkey;
3494 :
3495 : /* find definition of bank */
3496 : status = db_find_key(hDB, hKeyDef, bank_name, &hKeyVar);
3497 : if (status != DB_SUCCESS) {
3498 : cm_msg(MERROR, "root_book_bank", "received unknown bank \"%s\" in event #%d", bank_name, event_id);
3499 : return 0;
3500 : }
3501 :
3502 : if (et->n_branch + 1 == MAX_BANKS) {
3503 : cm_msg(MERROR, "root_book_bank", "max number of banks (%d) exceeded in event #%d", MAX_BANKS, event_id);
3504 : return 0;
3505 : }
3506 :
3507 : db_get_key(hDB, hKeyVar, &varkey);
3508 :
3509 : if (varkey.type != TID_KEY) {
3510 : /* book variable length array size */
3511 :
3512 : sprintf(str, "n%s/I:%s[n%s]/", varkey.name, varkey.name, varkey.name);
3513 :
3514 : switch (varkey.type) {
3515 : case TID_UINT8:
3516 : case TID_CHAR:
3517 : strcat(str, "b");
3518 : break;
3519 : case TID_INT8:
3520 : strcat(str, "B");
3521 : break;
3522 : case TID_UINT16:
3523 : strcat(str, "s");
3524 : break;
3525 : case TID_INT16:
3526 : strcat(str, "S");
3527 : break;
3528 : case TID_UINT32:
3529 : strcat(str, "i");
3530 : break;
3531 : case TID_INT32:
3532 : strcat(str, "I");
3533 : break;
3534 : case TID_BOOL:
3535 : strcat(str, "I");
3536 : break;
3537 : case TID_FLOAT:
3538 : strcat(str, "F");
3539 : break;
3540 : case TID_DOUBLE:
3541 : strcat(str, "D");
3542 : break;
3543 : case TID_STRING:
3544 : strcat(str, "C");
3545 : break;
3546 : }
3547 :
3548 : et->branch[et->n_branch] = et->tree->Branch(bank_name, 0, (const char*)str);
3549 : et->branch_name[et->n_branch] = *(DWORD *) bank_name;
3550 : et->n_branch++;
3551 : } else {
3552 : /* book structured bank */
3553 : str[0] = 0;
3554 :
3555 : for (i = 0;; i++) {
3556 : /* loop through bank variables */
3557 : status = db_enum_key(hDB, hKeyVar, i, &hKeySubVar);
3558 : if (status == DB_NO_MORE_SUBKEYS)
3559 : break;
3560 :
3561 : db_get_key(hDB, hKeySubVar, &subvarkey);
3562 :
3563 : if (i != 0)
3564 : strcat(str, ":");
3565 : strcat(str, subvarkey.name);
3566 : strcat(str, "/");
3567 : switch (subvarkey.type) {
3568 : case TID_UINT8:
3569 : case TID_CHAR:
3570 : strcat(str, "b");
3571 : break;
3572 : case TID_INT8:
3573 : strcat(str, "B");
3574 : break;
3575 : case TID_UINT16:
3576 : strcat(str, "s");
3577 : break;
3578 : case TID_INT16:
3579 : strcat(str, "S");
3580 : break;
3581 : case TID_UINT32:
3582 : strcat(str, "i");
3583 : break;
3584 : case TID_INT32:
3585 : strcat(str, "I");
3586 : break;
3587 : case TID_BOOL:
3588 : strcat(str, "I");
3589 : break;
3590 : case TID_FLOAT:
3591 : strcat(str, "F");
3592 : break;
3593 : case TID_DOUBLE:
3594 : strcat(str, "D");
3595 : break;
3596 : case TID_STRING:
3597 : strcat(str, "C");
3598 : break;
3599 : }
3600 : }
3601 :
3602 : et->branch[et->n_branch] = et->tree->Branch(bank_name, 0, (const char*)str);
3603 : et->branch_name[et->n_branch] = *(DWORD *) bank_name;
3604 : et->n_branch++;
3605 : }
3606 :
3607 : return 1;
3608 : }
3609 :
3610 : /*------------------------------------------------------------------*/
3611 :
3612 : INT root_write(LOG_CHN * log_chn, const EVENT_HEADER * pevent, INT evt_size)
3613 : {
3614 : INT i;
3615 : char bank_name[32];
3616 : BANK_HEADER *pbh;
3617 : void *pdata;
3618 : EVENT_TREE *et;
3619 : BANK *pbk;
3620 : BANK32 *pbk32;
3621 : BANK32A *pbk32a;
3622 : DWORD bklen;
3623 : DWORD bkname;
3624 : WORD bktype;
3625 : TBranch *branch;
3626 :
3627 : if ((pevent->event_id == EVENTID_BOR) ||
3628 : (pevent->event_id == EVENTID_EOR) ||
3629 : (pevent->event_id == EVENTID_MESSAGE) ||
3630 : (pevent->event_id == EVENTID_FRAG1) ||
3631 : (pevent->event_id == EVENTID_FRAG)) {
3632 : // Cannot write system event into ROOT file
3633 : return 0;
3634 : }
3635 :
3636 : EVENT_DEF *event_def = db_get_event_definition(pevent->event_id);
3637 : if (event_def == NULL) {
3638 : cm_msg(MERROR, "root_write", "Definition for event #%d not found under /Equipment", pevent->event_id);
3639 : return -1;
3640 : }
3641 :
3642 : TREE_STRUCT *ts = log_chn->root_tree_struct;
3643 :
3644 : /*---- MIDAS format ----------------------------------------------*/
3645 :
3646 : if (event_def->format == FORMAT_MIDAS) {
3647 : pbh = (BANK_HEADER *) (pevent + 1);
3648 : bk_swap(pbh, FALSE);
3649 :
3650 : /* find event in tree structure */
3651 : for (i = 0; i < ts->n_tree; i++)
3652 : if (ts->event_tree[i].event_id == pevent->event_id)
3653 : break;
3654 :
3655 : if (i == ts->n_tree) {
3656 : cm_msg(MERROR, "root_write", "Event #%d not booked by root_book_events()", pevent->event_id);
3657 : return -1;
3658 : }
3659 :
3660 : et = ts->event_tree + i;
3661 :
3662 : /* first mark all banks non-filled */
3663 : for (i = 0; i < et->n_branch; i++)
3664 : et->branch_filled[i] = FALSE;
3665 :
3666 : /* go thourgh all banks and set the address */
3667 : pbk = NULL;
3668 : pbk32 = NULL;
3669 : pbk32a = NULL;
3670 : do {
3671 : /* scan all banks */
3672 : if (bk_is32a(pbh)) {
3673 : bklen = bk_iterate32a(pbh, &pbk32a, &pdata);
3674 : if (pbk32a == NULL)
3675 : break;
3676 : bkname = *((DWORD *) pbk32a->name);
3677 : bktype = (WORD) pbk32a->type;
3678 : } else if (bk_is32(pbh)) {
3679 : bklen = bk_iterate32(pbh, &pbk32, &pdata);
3680 : if (pbk32 == NULL)
3681 : break;
3682 : bkname = *((DWORD *) pbk32->name);
3683 : bktype = (WORD) pbk32->type;
3684 : } else {
3685 : bklen = bk_iterate(pbh, &pbk, &pdata);
3686 : if (pbk == NULL)
3687 : break;
3688 : bkname = *((DWORD *) pbk->name);
3689 : bktype = (WORD) pbk->type;
3690 : }
3691 :
3692 : if (rpc_tid_size(bktype & 0xFF))
3693 : bklen /= rpc_tid_size(bktype & 0xFF);
3694 :
3695 : *((DWORD *) bank_name) = bkname;
3696 : bank_name[4] = 0;
3697 :
3698 : for (i = 0; i < et->n_branch; i++)
3699 : if (et->branch_name[i] == bkname)
3700 : break;
3701 :
3702 : if (i == et->n_branch)
3703 : root_book_bank(et, event_def->hDefKey, pevent->event_id, bank_name);
3704 :
3705 : branch = et->branch[i];
3706 : et->branch_filled[i] = TRUE;
3707 : et->branch_len[i] = bklen;
3708 :
3709 : if (bktype != TID_STRUCT) {
3710 : TIter next(branch->GetListOfLeaves());
3711 : TLeaf *leaf = (TLeaf *) next();
3712 :
3713 : /* varibale length array */
3714 : leaf->SetAddress(&et->branch_len[i]);
3715 :
3716 : leaf = (TLeaf *) next();
3717 : leaf->SetAddress(pdata);
3718 : } else {
3719 : /* structured bank */
3720 : branch->SetAddress(pdata);
3721 : }
3722 :
3723 : } while (1);
3724 :
3725 : /* check if all branches have been filled */
3726 : for (i = 0; i < et->n_branch; i++)
3727 : if (!et->branch_filled[i])
3728 : cm_msg(MERROR, "root_write", "Bank %s booked but not received, tree cannot be filled", bank_name);
3729 :
3730 : /* fill tree */
3731 : et->tree->Fill();
3732 : }
3733 :
3734 : return (INT) ts->f->GetBytesWritten();
3735 : }
3736 :
3737 : /*------------------------------------------------------------------*/
3738 :
3739 : INT root_log_open(LOG_CHN * log_chn, INT run_number)
3740 : {
3741 : INT size, level;
3742 : char str[256+100], name[256];
3743 : TREE_STRUCT *tree_struct;
3744 :
3745 : /* Create device channel */
3746 : if (log_chn->type == LOG_TYPE_FTP) {
3747 : cm_msg(MERROR, "root_log_open", "ROOT files can only reside on disk");
3748 : log_chn->handle = 0;
3749 : return -1;
3750 : } else {
3751 : /* check if file exists */
3752 : if (strstr(log_chn->path.c_str(), "null") == NULL) {
3753 : log_chn->handle = open(log_chn->path.c_str(), O_RDONLY);
3754 : if (log_chn->handle > 0) {
3755 : /* check if file length is nonzero */
3756 : if (lseek(log_chn->handle, 0, SEEK_END) > 0) {
3757 : close(log_chn->handle);
3758 : log_chn->handle = 0;
3759 : return SS_FILE_EXISTS;
3760 : }
3761 : }
3762 : }
3763 :
3764 : name[0] = 0;
3765 : size = sizeof(name);
3766 : db_get_value(hDB, 0, "/Experiment/Name", name, &size, TID_STRING, TRUE);
3767 :
3768 : sprintf(str, "MIDAS exp. %s, run #%d", name, run_number);
3769 :
3770 : TFile *f = new TFile(log_chn->path.c_str(), "create", str, 1);
3771 : if (!f->IsOpen()) {
3772 : delete f;
3773 : log_chn->handle = 0;
3774 : return SS_FILE_ERROR;
3775 : }
3776 : log_chn->handle = 1;
3777 :
3778 : /* set compression level */
3779 : level = 0;
3780 : size = sizeof(level);
3781 : db_get_value(hDB, log_chn->settings_hkey, "Compression", &level, &size, TID_INT32, FALSE);
3782 : f->SetCompressionLevel(level);
3783 :
3784 : /* create root structure with trees and branches */
3785 : tree_struct = (TREE_STRUCT *) malloc(sizeof(TREE_STRUCT));
3786 :
3787 : assert(tree_struct != NULL);
3788 :
3789 : tree_struct->f = f;
3790 :
3791 : /* book event tree */
3792 : root_book_trees(tree_struct);
3793 :
3794 : /* store file object in format_info */
3795 : log_chn->root_tree_struct = tree_struct;
3796 : }
3797 :
3798 : #if 0
3799 : /* write ODB dump */
3800 : if (log_chn->settings.odb_dump) {
3801 : EVENT_HEADER event;
3802 :
3803 : event.event_id = EVENTID_BOR;
3804 : event.data_size = 0;
3805 : event.serial_number = run_number;
3806 :
3807 : //root_write(log_chn, &event, sizeof(EVENT_HEADER));
3808 : }
3809 : #endif
3810 :
3811 : return SS_SUCCESS;
3812 : }
3813 :
3814 : /*------------------------------------------------------------------*/
3815 :
3816 : INT root_log_close(LOG_CHN * log_chn, INT run_number)
3817 : {
3818 : TREE_STRUCT *ts = log_chn->root_tree_struct;
3819 :
3820 : /* flush and close file */
3821 : ts->f->Write();
3822 : ts->f->Close();
3823 : delete ts->f; // deletes also all trees and branches!
3824 :
3825 : /* delete event tree */
3826 : free(ts->event_tree);
3827 : ts->event_tree = NULL;
3828 : free(ts);
3829 : ts = NULL;
3830 :
3831 : log_chn->root_tree_struct = NULL;
3832 :
3833 : return SS_SUCCESS;
3834 : }
3835 :
3836 : class WriterROOT : public WriterInterface
3837 : {
3838 : public:
3839 : WriterROOT(LOG_CHN* log_chn) // ctor
3840 : {
3841 : if (fTrace)
3842 : printf("WriterROOT: path [%s]\n", log_chn->path.c_str());
3843 : }
3844 :
3845 : ~WriterROOT() // dtor
3846 : {
3847 : if (fTrace)
3848 : printf("WriterROOT: destructor\n");
3849 : }
3850 :
3851 : int wr_open(LOG_CHN* log_chn, int run_number)
3852 : {
3853 : fBytesIn = 0;
3854 : fBytesOut = 0;
3855 :
3856 : if (fTrace)
3857 : printf("WriterROOT: open path [%s]\n", log_chn->path.c_str());
3858 :
3859 : int status = root_log_open(log_chn, run_number);
3860 : if (status != SUCCESS)
3861 : return status;
3862 :
3863 : log_chn->handle = 9999;
3864 :
3865 : return SUCCESS;
3866 : }
3867 :
3868 : int wr_write(LOG_CHN* log_chn, const void* data, const int size)
3869 : {
3870 : if (fTrace)
3871 : printf("WriterROOT: write path [%s], size %d\n", log_chn->path.c_str(), size);
3872 :
3873 : if (size == 0)
3874 : return SUCCESS;
3875 :
3876 : fBytesIn += size;
3877 :
3878 : int written = root_write(log_chn, (const EVENT_HEADER*)data, size);
3879 :
3880 : if (written < 0) {
3881 : return SS_FILE_ERROR;
3882 : }
3883 :
3884 : fBytesOut += size;
3885 :
3886 : return SUCCESS;
3887 : }
3888 :
3889 : int wr_close(LOG_CHN* log_chn, int run_number)
3890 : {
3891 : if (fTrace)
3892 : printf("WriterROOT: close path [%s]\n", log_chn->path.c_str());
3893 :
3894 : int status = root_log_close(log_chn, run_number);
3895 :
3896 : log_chn->handle = 0;
3897 :
3898 : return status;
3899 : }
3900 :
3901 : std::string wr_get_file_ext()
3902 : {
3903 : return ".root";
3904 : }
3905 :
3906 : std::string wr_get_chain()
3907 : {
3908 : return "ROOT";
3909 : }
3910 :
3911 : private:
3912 : };
3913 :
3914 : #endif /* HAVE_ROOT */
3915 :
3916 : /*------------------------------------------------------------------*/
3917 :
3918 0 : WriterInterface* NewWriterBzip2(LOG_CHN* log_chn)
3919 : {
3920 0 : std::string bzip2_command = "bzip2 -z";
3921 :
3922 0 : if (log_chn->settings.bzip2_compression) {
3923 0 : bzip2_command += " -";
3924 0 : bzip2_command += IntToString(log_chn->settings.bzip2_compression);
3925 : }
3926 :
3927 0 : return new WriterPopen(log_chn, (bzip2_command + " > ").c_str(), ".bz2");
3928 0 : }
3929 :
3930 0 : WriterInterface* NewWriterPbzip2(LOG_CHN* log_chn)
3931 : {
3932 0 : std::string pbzip2_command = "pbzip2 -c -z";
3933 :
3934 0 : if (log_chn->settings.pbzip2_num_cpu) {
3935 0 : pbzip2_command += " -p";
3936 0 : pbzip2_command += IntToString(log_chn->settings.pbzip2_num_cpu);
3937 : }
3938 :
3939 0 : if (log_chn->settings.pbzip2_compression) {
3940 0 : pbzip2_command += " -";
3941 0 : pbzip2_command += IntToString(log_chn->settings.pbzip2_compression);
3942 : }
3943 :
3944 0 : if (strlen(log_chn->settings.pbzip2_options) > 0) {
3945 0 : pbzip2_command += " ";
3946 0 : pbzip2_command += log_chn->settings.pbzip2_options;
3947 : }
3948 :
3949 0 : return new WriterPopen(log_chn, (pbzip2_command + " > ").c_str(), ".bz2");
3950 0 : }
3951 :
3952 : #define CHECKSUM_NONE 0
3953 : #define CHECKSUM_ZLIB 1
3954 : #define CHECKSUM_CRC32C 2
3955 : #define CHECKSUM_SHA256 3
3956 : #define CHECKSUM_SHA512 4
3957 :
3958 0 : WriterInterface* NewChecksum(LOG_CHN* log_chn, int code, int level, WriterInterface* chained)
3959 : {
3960 0 : if (code == CHECKSUM_NONE) {
3961 0 : return chained;
3962 0 : } else if (code == CHECKSUM_ZLIB) {
3963 0 : return new WriterCRC32Zlib(log_chn, level, chained);
3964 0 : } else if (code == CHECKSUM_CRC32C) {
3965 0 : return new WriterCRC32C(log_chn, level, chained);
3966 0 : } else if (code == CHECKSUM_SHA256) {
3967 0 : return new WriterSHA256(log_chn, level, chained);
3968 0 : } else if (code == CHECKSUM_SHA512) {
3969 0 : return new WriterSHA512(log_chn, level, chained);
3970 : } else {
3971 0 : cm_msg(MERROR, "log_create_writer", "channel %s unknown checksum code %d", log_chn->path.c_str(), code);
3972 0 : return chained;
3973 : }
3974 : }
3975 :
3976 : #define COMPRESS_NONE 0
3977 : #define COMPRESS_ZLIB 1
3978 : #define COMPRESS_LZ4 2
3979 : #define COMPRESS_BZIP2 3
3980 : #define COMPRESS_PBZIP2 4
3981 :
3982 0 : WriterInterface* NewCompression(LOG_CHN* log_chn, int code, WriterInterface* chained)
3983 : {
3984 0 : if (code == COMPRESS_NONE) {
3985 0 : return chained;
3986 0 : } else if (code == COMPRESS_LZ4) {
3987 0 : return new WriterLZ4(log_chn, chained);
3988 : } else {
3989 0 : cm_msg(MERROR, "log_create_writer", "channel %s unknown compression code %d", log_chn->path.c_str(), code);
3990 0 : return chained;
3991 : }
3992 : }
3993 :
3994 : #define OUTPUT_NONE 0
3995 : #define OUTPUT_NULL 1
3996 : #define OUTPUT_FILE 2
3997 : #define OUTPUT_FTP 3
3998 : #define OUTPUT_ROOT 4
3999 : #define OUTPUT_PIPE 5
4000 :
4001 0 : std::string get_value(HNDLE hDB, HNDLE hDir, const char* name)
4002 : {
4003 : char value[MAX_STRING_LENGTH];
4004 0 : value[0] = 0;
4005 0 : int size = sizeof(value);
4006 0 : int status = db_get_value(hDB, hDir, name, &value, &size, TID_STRING, FALSE);
4007 0 : if (status != DB_SUCCESS)
4008 0 : return "";
4009 0 : return value;
4010 : }
4011 :
4012 0 : void set_value(HNDLE hDB, HNDLE hDir, const char* name, const std::string& set, const std::string& def)
4013 : {
4014 0 : std::string s = set + " (one of:" + def + ")";
4015 0 : int size = 256; // MUST match record definition // strlen(value);
4016 0 : s.reserve(size);
4017 0 : const char* value = s.c_str();
4018 0 : db_set_value(hDB, hDir, name, value, size, 1, TID_STRING);
4019 0 : }
4020 :
4021 0 : int check_add(int v, int n, const std::string& val, const char* str, bool bdef, std::string* def, std::string* sel)
4022 : {
4023 0 : (*def) += std::string(" ") + str;
4024 0 : if (v)
4025 0 : return v; // keep returning the first selection
4026 0 : if (val.find(str) == 0) {
4027 0 : *sel = str;
4028 0 : return n; // if no selection yet, return the new selection
4029 : }
4030 0 : return v;
4031 : }
4032 :
4033 0 : int select_checksum_module(HNDLE hDB, HNDLE hSet, const char* name)
4034 : {
4035 0 : std::string val = get_value(hDB, hSet, name);
4036 0 : std::string sel;
4037 0 : std::string def;
4038 0 : int s = 0;
4039 0 : s = check_add(s, CHECKSUM_NONE, val, "NONE", false, &def, &sel);
4040 0 : s = check_add(s, CHECKSUM_CRC32C, val, "CRC32C", true, &def, &sel);
4041 0 : s = check_add(s, CHECKSUM_SHA256, val, "SHA256", false, &def, &sel);
4042 0 : s = check_add(s, CHECKSUM_SHA512, val, "SHA512", false, &def, &sel);
4043 0 : s = check_add(s, CHECKSUM_ZLIB, val, "ZLIB", false, &def, &sel);
4044 0 : if (sel == "")
4045 0 : sel = "NONE";
4046 : //set_value(hDB, hSet, name, sel, def);
4047 0 : return s;
4048 0 : }
4049 :
4050 0 : int select_compression_module(HNDLE hDB, HNDLE hSet, const char* name)
4051 : {
4052 0 : std::string val = get_value(hDB, hSet, name);
4053 0 : std::string sel;
4054 0 : std::string def;
4055 0 : int s = 0;
4056 0 : s = check_add(s, COMPRESS_NONE, val, "none", false, &def, &sel);
4057 0 : s = check_add(s, COMPRESS_ZLIB, val, "gzip", true, &def, &sel);
4058 0 : s = check_add(s, COMPRESS_LZ4, val, "lz4", false, &def, &sel);
4059 0 : s = check_add(s, COMPRESS_BZIP2, val, "bzip2", false, &def, &sel);
4060 0 : s = check_add(s, COMPRESS_PBZIP2, val, "pbzip2", false, &def, &sel);
4061 0 : if (sel == "")
4062 0 : sel = "none";
4063 : //set_value(hDB, hSet, name, sel, def);
4064 0 : return s;
4065 0 : }
4066 :
4067 0 : int select_output_module(HNDLE hDB, HNDLE hSet, const char* name)
4068 : {
4069 0 : std::string val = get_value(hDB, hSet, name);
4070 0 : std::string sel;
4071 0 : std::string def;
4072 0 : int s = 0;
4073 0 : s = check_add(s, OUTPUT_NULL, val, "NULL", false, &def, &sel);
4074 0 : s = check_add(s, OUTPUT_FILE, val, "FILE", true, &def, &sel);
4075 0 : s = check_add(s, OUTPUT_FTP, val, "FTP", false, &def, &sel);
4076 0 : s = check_add(s, OUTPUT_ROOT, val, "ROOT", false, &def, &sel);
4077 0 : s = check_add(s, OUTPUT_PIPE, val, "PIPE", false, &def, &sel);
4078 0 : if (sel == "")
4079 0 : sel = "FILE";
4080 : //set_value(hDB, hSet, name, sel, def);
4081 0 : return s;
4082 0 : }
4083 :
4084 0 : int log_create_writer(LOG_CHN *log_chn)
4085 : {
4086 0 : assert(log_chn->writer == NULL);
4087 0 : log_chn->writer = NULL;
4088 :
4089 0 : if (log_chn->output_module > 0) {
4090 :
4091 0 : if (log_chn->compression_module == COMPRESS_ZLIB) {
4092 :
4093 0 : if (log_chn->output_module != OUTPUT_FILE) {
4094 0 : cm_msg(MERROR, "log_create_writer", "channel %s requested GZIP/ZLIB compression, output module must be FILE", log_chn->path.c_str());
4095 0 : return SS_FILE_ERROR;
4096 : }
4097 :
4098 0 : log_chn->writer = new WriterGzip(log_chn, 0);
4099 0 : log_chn->do_disk_level = TRUE;
4100 : }
4101 0 : else if (log_chn->compression_module == COMPRESS_BZIP2) {
4102 :
4103 0 : if (log_chn->output_module != OUTPUT_FILE) {
4104 0 : cm_msg(MERROR, "log_create_writer", "channel %s requested BZIP2 compression, output module must be FILE", log_chn->path.c_str());
4105 0 : return SS_FILE_ERROR;
4106 : }
4107 :
4108 0 : log_chn->writer = NewWriterBzip2(log_chn);
4109 0 : log_chn->do_disk_level = TRUE;
4110 :
4111 : }
4112 0 : else if (log_chn->compression_module == COMPRESS_PBZIP2) {
4113 :
4114 0 : if (log_chn->output_module != OUTPUT_FILE) {
4115 0 : cm_msg(MERROR, "log_create_writer", "channel %s requested PBZIP2 compression, output module must be FILE", log_chn->path.c_str());
4116 0 : return SS_FILE_ERROR;
4117 : }
4118 :
4119 0 : log_chn->writer = NewWriterPbzip2(log_chn);
4120 0 : log_chn->do_disk_level = TRUE;
4121 : }
4122 0 : else if (log_chn->output_module == OUTPUT_NULL) {
4123 :
4124 0 : log_chn->writer = new WriterNull(log_chn);
4125 0 : log_chn->do_disk_level = TRUE;
4126 : }
4127 0 : else if (log_chn->output_module == OUTPUT_FILE) {
4128 :
4129 0 : log_chn->writer = NewCompression(log_chn, log_chn->compression_module, NewChecksum(log_chn, log_chn->post_checksum_module, 0, new WriterFile(log_chn)));
4130 0 : log_chn->do_disk_level = TRUE;
4131 : }
4132 0 : else if (log_chn->output_module == OUTPUT_FTP) {
4133 :
4134 0 : log_chn->writer = NewCompression(log_chn, log_chn->compression_module, NewChecksum(log_chn, log_chn->post_checksum_module, 0, new WriterFtp(log_chn)));
4135 0 : log_chn->do_disk_level = FALSE;
4136 0 : log_chn->statistics.disk_level = -1;
4137 : }
4138 0 : else if (log_chn->output_module == OUTPUT_ROOT) {
4139 :
4140 : #ifdef HAVE_ROOT
4141 : log_chn->writer = new WriterROOT(log_chn);
4142 : log_chn->do_disk_level = TRUE;
4143 : #else
4144 0 : cm_msg(MERROR, "log_create_writer", "channel \"%s\" requested ROOT output, but mlogger is built without HAVE_ROOT", log_chn->name.c_str());
4145 0 : log_chn->writer = new WriterNull(log_chn);
4146 0 : log_chn->do_disk_level = TRUE;
4147 : #endif
4148 : }
4149 0 : else if (log_chn->output_module == OUTPUT_PIPE) {
4150 :
4151 0 : log_chn->writer = NewCompression(log_chn, log_chn->compression_module, NewChecksum(log_chn, log_chn->post_checksum_module, 0, new WriterPopen(log_chn, "xxx", "")));
4152 0 : log_chn->do_disk_level = FALSE;
4153 0 : log_chn->statistics.disk_level = -1;
4154 : }
4155 :
4156 : //log_chn->writer = new WriterROOT(log_chn);
4157 : //log_chn->do_disk_level = TRUE;
4158 :
4159 0 : if (log_chn->pre_checksum_module) {
4160 0 : log_chn->writer = NewChecksum(log_chn, log_chn->pre_checksum_module, 1, log_chn->writer);
4161 : }
4162 :
4163 : //cm_msg(MINFO, "log_create_writer", "channel \"%s\" writer chain: %s", log_chn->path.c_str(), log_chn->writer->wr_get_chain().c_str());
4164 :
4165 0 : return SUCCESS;
4166 : }
4167 :
4168 0 : cm_msg(MERROR, "log_create_writer", "channel %s invalid output module value %d", log_chn->path.c_str(), log_chn->output_module);
4169 0 : return SS_FILE_ERROR;
4170 :
4171 : #ifdef OBSOLETE
4172 : int xcompress = log_chn->compression;
4173 : // compression format: ABNNN
4174 : // A - pre-compression checksum,
4175 : // B - post-compression (file) checksum,
4176 : // NNN - compression code
4177 : int compression = xcompress%1000;
4178 : int prechecksum = (xcompress/10000)%10;
4179 : int postchecksum = (xcompress/1000)%10;
4180 :
4181 : // 0=old file output, 1-9=old gzip output
4182 : if (compression < 10)
4183 : return SUCCESS;
4184 :
4185 : if (compression==80) {
4186 : #ifdef HAVE_ROOT
4187 : log_chn->writer = new WriterROOT(log_chn);
4188 : log_chn->do_disk_level = TRUE;
4189 : #else
4190 : log_chn->writer = new WriterNull(log_chn);
4191 : log_chn->do_disk_level = TRUE;
4192 : #endif
4193 : } else if (compression==81) {
4194 : log_chn->writer = new WriterFtp(log_chn);
4195 : log_chn->do_disk_level = FALSE;
4196 : log_chn->statistics.disk_level = -1;
4197 : } else if (compression==82) {
4198 : log_chn->writer = new WriterLZ4(log_chn, NewChecksum(log_chn, postchecksum, 0, new WriterFtp(log_chn)));
4199 : log_chn->do_disk_level = FALSE;
4200 : log_chn->statistics.disk_level = -1;
4201 : } else if (compression==98) {
4202 : log_chn->writer = new WriterNull(log_chn);
4203 : log_chn->do_disk_level = TRUE;
4204 : } else if (compression==99) {
4205 : log_chn->writer = new WriterFile(log_chn);
4206 : log_chn->do_disk_level = TRUE;
4207 : } else if (compression==100) {
4208 : log_chn->writer = new WriterLZ4(log_chn, NewChecksum(log_chn, postchecksum, 0, new WriterFile(log_chn)));
4209 : log_chn->do_disk_level = TRUE;
4210 : } else if (compression==200) {
4211 : log_chn->writer = NewWriterBzip2(log_chn);
4212 : log_chn->do_disk_level = TRUE;
4213 : } else if (compression==201) {
4214 : log_chn->writer = NewWriterPbzip2(log_chn);
4215 : log_chn->do_disk_level = TRUE;
4216 : } else if (compression==300) {
4217 : log_chn->writer = new WriterGzip(log_chn, 0);
4218 : log_chn->do_disk_level = TRUE;
4219 : } else if (compression==301) {
4220 : log_chn->writer = new WriterGzip(log_chn, 1);
4221 : log_chn->do_disk_level = TRUE;
4222 : } else if (compression==309) {
4223 : log_chn->writer = new WriterGzip(log_chn, 9);
4224 : log_chn->do_disk_level = TRUE;
4225 : } else {
4226 : cm_msg(MERROR, "log_create_writer", "channel %s unknown compression mode %d", log_chn->path.c_str(), log_chn->compression);
4227 : return SS_FILE_ERROR;
4228 : }
4229 :
4230 : if (prechecksum) {
4231 : log_chn->writer = NewChecksum(log_chn, prechecksum, 1, log_chn->writer);
4232 : }
4233 : #endif
4234 :
4235 : return SS_SUCCESS;
4236 : }
4237 :
4238 : /*---- log_open ----------------------------------------------------*/
4239 :
4240 0 : INT log_open(LOG_CHN * log_chn, INT run_number)
4241 : {
4242 0 : INT status = SUCCESS;
4243 :
4244 0 : log_chn->last_checked = ss_millitime();
4245 :
4246 0 : if (log_chn->writer) {
4247 0 : WriterInterface* wr = log_chn->writer;
4248 :
4249 0 : int status = wr->wr_open(log_chn, run_number);
4250 :
4251 0 : if (status != SUCCESS)
4252 0 : return status;
4253 :
4254 : /* write ODB dump */
4255 0 : if (log_chn->settings.odb_dump)
4256 0 : log_odb_dump(log_chn, EVENTID_BOR, run_number);
4257 :
4258 : /* update statistics */
4259 0 : double incr = wr->fBytesOut - log_chn->statistics.bytes_written_subrun;
4260 0 : if (incr < 0)
4261 0 : incr = 0;
4262 :
4263 : //printf("bytes out %f, incr %f, subrun %f, written %f, total %f (log_open)\n", wr->fBytesOut, incr, log_chn->statistics.bytes_written_subrun, log_chn->statistics.bytes_written, log_chn->statistics.bytes_written_total);
4264 :
4265 0 : log_chn->statistics.bytes_written += incr;
4266 0 : log_chn->statistics.bytes_written_subrun = wr->fBytesOut;
4267 0 : log_chn->statistics.bytes_written_total += incr;
4268 : } else {
4269 0 : return SS_INVALID_FORMAT;
4270 : }
4271 : #ifdef OBSOLETE
4272 : } else if (equal_ustring(log_chn->settings.format, "ROOT")) {
4273 : #ifdef HAVE_ROOT
4274 : log_chn->format = FORMAT_ROOT;
4275 : status = root_log_open(log_chn, run_number);
4276 : #else
4277 : return SS_NO_ROOT;
4278 : #endif
4279 : } else if (equal_ustring(log_chn->settings.format, "MIDAS")) {
4280 : log_chn->format = FORMAT_MIDAS;
4281 : status = midas_log_open(log_chn, run_number);
4282 : } else
4283 : return SS_INVALID_FORMAT;
4284 : #endif
4285 0 : return status;
4286 : }
4287 :
4288 : /*---- log_close ---------------------------------------------------*/
4289 :
4290 0 : INT log_close(LOG_CHN * log_chn, INT run_number)
4291 : {
4292 0 : if (log_chn->writer) {
4293 : /* write ODB dump */
4294 0 : if (log_chn->settings.odb_dump)
4295 0 : log_odb_dump(log_chn, EVENTID_EOR, run_number);
4296 :
4297 0 : WriterInterface* wr = log_chn->writer;
4298 :
4299 0 : wr->wr_close(log_chn, run_number);
4300 :
4301 : /* update statistics */
4302 :
4303 0 : double incr = wr->fBytesOut - log_chn->statistics.bytes_written_subrun;
4304 0 : if (incr < 0)
4305 0 : incr = 0;
4306 :
4307 : //printf("bytes out %f, incr %f, subrun %f, written %f, total %f (log_close)\n", wr->fBytesOut, incr, log_chn->statistics.bytes_written_subrun, log_chn->statistics.bytes_written, log_chn->statistics.bytes_written_total);
4308 :
4309 0 : log_chn->statistics.bytes_written += incr;
4310 0 : log_chn->statistics.bytes_written_subrun = wr->fBytesOut;
4311 0 : log_chn->statistics.bytes_written_total += incr;
4312 : }
4313 : #ifdef OBSOLETE
4314 : #ifdef HAVE_ROOT
4315 : } else if (log_chn->format == FORMAT_ROOT) {
4316 : root_log_close(log_chn, run_number);
4317 : #endif
4318 : } else if (log_chn->format == FORMAT_MIDAS) {
4319 : midas_log_close(log_chn, run_number);
4320 : }
4321 : #endif
4322 :
4323 : /* if file name starts with '.', rename it */
4324 : char str[256]; // FIXME: this will truncate the filename of the output file. K.O.
4325 0 : mstrlcpy(str, log_chn->path.c_str(), sizeof(str));
4326 0 : char* p = str;
4327 0 : if (strrchr(str, DIR_SEPARATOR)) {
4328 0 : p = strrchr(str, DIR_SEPARATOR)+1;
4329 : }
4330 0 : if (*p == '.') {
4331 0 : mstrlcpy(p, p+1, sizeof(str));
4332 0 : rename(log_chn->path.c_str(), str); // FIXME: must check return status. K.O.
4333 : }
4334 :
4335 0 : log_chn->statistics.files_written += 1;
4336 0 : log_chn->handle = 0;
4337 0 : log_chn->ftp_con = NULL;
4338 :
4339 0 : if (log_chn->writer) {
4340 0 : delete log_chn->writer;
4341 0 : log_chn->writer = NULL;
4342 : }
4343 :
4344 0 : return SS_SUCCESS;
4345 : }
4346 :
4347 : /*---- log disk levels ---------------------------------------------*/
4348 :
4349 0 : int log_disk_level(LOG_CHN* log_chn, double* pdisk_size, double* pdisk_free)
4350 : {
4351 0 : std::string str = log_chn->path.c_str();
4352 0 : size_t pos = str.rfind('/');
4353 0 : if (pos != std::string::npos) {
4354 0 : str.erase(pos); // strip filename for bzip2
4355 : }
4356 :
4357 : //printf("log_disk_level [%s] [%s]\n", log_chn->path.c_str(), str.c_str());
4358 :
4359 0 : double MiB = 1024*1024;
4360 0 : double disk_size = ss_disk_size(str.c_str());
4361 0 : double disk_free = ss_disk_free(str.c_str());
4362 0 : double limit = 10E6;
4363 0 : double level = 1.0-disk_free/disk_size;
4364 :
4365 0 : if (pdisk_size)
4366 0 : *pdisk_size = disk_size; // should be in statistics
4367 0 : if (pdisk_free)
4368 0 : *pdisk_free = disk_free; // should be in statistics
4369 :
4370 0 : log_chn->statistics.disk_level = level;
4371 :
4372 0 : if (verbose)
4373 0 : printf("log_disk_level: channel path [%s], disk_size %1.0lf MiB, disk_free %1.0lf MiB, limit %1.0f MiB, disk level %.1f%%\n", log_chn->path.c_str(), disk_size/MiB, disk_free/MiB, limit/MiB, level*100.0);
4374 :
4375 0 : return SUCCESS;
4376 0 : }
4377 :
4378 0 : int maybe_check_disk_level()
4379 : {
4380 0 : DWORD actual_time = ss_millitime();
4381 : static DWORD last_check_time = 0;
4382 :
4383 0 : if (last_check_time == 0)
4384 0 : last_check_time = actual_time;
4385 :
4386 0 : if (actual_time - last_check_time < DISK_CHECK_INTERVAL_MILLISEC)
4387 0 : return SUCCESS;
4388 :
4389 0 : last_check_time = actual_time;
4390 :
4391 0 : for (unsigned i = 0; i < log_channels.size(); i++) {
4392 0 : LOG_CHN* chn = log_channels[i];
4393 :
4394 0 : if (!chn->do_disk_level)
4395 0 : continue;
4396 :
4397 0 : log_disk_level(chn, NULL, NULL);
4398 : }
4399 :
4400 0 : return SUCCESS;
4401 : }
4402 :
4403 0 : static int get_trans_flag()
4404 : {
4405 : int status, size, flag;
4406 :
4407 0 : size = sizeof(BOOL);
4408 0 : flag = FALSE;
4409 0 : status = db_get_value(hDB, 0, "/Logger/Async transitions", &flag, &size, TID_BOOL, FALSE);
4410 :
4411 0 : if (status == DB_SUCCESS) {
4412 0 : cm_msg(MINFO, "get_trans_flag", "ODB \"/Logger/Async transitions\" is obsolete, please delete it");
4413 : }
4414 :
4415 0 : size = sizeof(BOOL);
4416 0 : flag = TRUE;
4417 0 : status = db_get_value(hDB, 0, "/Logger/Multithread transitions", &flag, &size, TID_BOOL, FALSE);
4418 :
4419 0 : if (status == DB_SUCCESS) {
4420 0 : cm_msg(MINFO, "get_trans_flag", "ODB \"/Logger/Multithread transitions\" is obsolete, please delete it");
4421 : }
4422 :
4423 0 : size = sizeof(BOOL);
4424 0 : flag = FALSE;
4425 0 : db_get_value(hDB, 0, "/Logger/Detached transitions", &flag, &size, TID_BOOL, TRUE);
4426 :
4427 : // NB: we must use multithread or detached (via mtransition) transition
4428 : // otherwise we deadlock against frontends: they are writing to the SYSTEM
4429 : // buffer but we (mlogger) are not reading from it. if SYSTEM buffer is full,
4430 : // clients get stuck waiting for free space and cannot not handle TR_STOP RPC.
4431 : // if they could handle, it, they would still be stuck in bm_flush_cache()
4432 : // because we (mlogger) are not reading the SYSTEM buffer and these last
4433 : // events have nowhere to go. K.O.
4434 :
4435 0 : if (flag)
4436 0 : return TR_DETACH;
4437 : else
4438 0 : return TR_MTHREAD;
4439 : }
4440 :
4441 : /*---- log_write ---------------------------------------------------*/
4442 :
4443 0 : int stop_the_run(int restart)
4444 : {
4445 : int status, flag, size;
4446 : char errstr[256];
4447 :
4448 0 : if (restart) {
4449 0 : size = sizeof(BOOL);
4450 0 : flag = FALSE;
4451 0 : db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
4452 :
4453 0 : if (flag) {
4454 0 : start_requested = TRUE;
4455 0 : auto_restart = 0;
4456 : }
4457 : }
4458 :
4459 0 : stop_requested = TRUE;
4460 0 : stop_try_later = 0;
4461 :
4462 0 : int trans_flag = get_trans_flag();
4463 :
4464 0 : status = cm_transition(TR_STOP, 0, errstr, sizeof(errstr), trans_flag, verbose);
4465 0 : if (status == CM_TRANSITION_IN_PROGRESS) {
4466 0 : cm_msg(MERROR, "stop_the_run", "another transition is in progress, will try again later");
4467 0 : stop_requested = FALSE;
4468 0 : stop_try_later = ss_time_sec() + 10.0;
4469 0 : } else if (status != CM_SUCCESS) {
4470 0 : cm_msg(MERROR, "stop_the_run", "cannot stop the run, cm_transition() status %d, error: %s", status, errstr);
4471 0 : return status;
4472 : }
4473 :
4474 0 : return status;
4475 : }
4476 :
4477 0 : int start_the_run()
4478 : {
4479 : int status, size, state, run_number, flag;
4480 : char errstr[256];
4481 :
4482 0 : start_requested = FALSE;
4483 0 : auto_restart = 0;
4484 :
4485 : /* check if autorestart is still on */
4486 0 : size = sizeof(BOOL);
4487 0 : flag = FALSE;
4488 0 : db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
4489 :
4490 0 : if (!flag) {
4491 0 : cm_msg(MINFO, "start_the_run", "Run auto restart canceled");
4492 0 : return SUCCESS;
4493 : }
4494 :
4495 : /* check if really stopped */
4496 0 : size = sizeof(state);
4497 0 : status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4498 0 : if (status != DB_SUCCESS) {
4499 0 : cm_msg(MERROR, "start_the_run", "cannot get Runinfo/State in database, db_get_value() status %d", status);
4500 0 : return status;
4501 : }
4502 :
4503 : static int backoff = 1;
4504 :
4505 0 : if (state != STATE_STOPPED) {
4506 0 : cm_msg(MINFO, "start_the_run", "Runinfo/State %d is not STATE_STOPPED, will try again in %d seconds", state, backoff);
4507 0 : auto_restart = ss_time() + backoff; /* try again later */
4508 0 : if (backoff < 1)
4509 0 : backoff = 1;
4510 0 : else if (backoff > 1*60)
4511 0 : backoff = 1*60;
4512 : else
4513 0 : backoff *= 2;
4514 0 : return SUCCESS;
4515 : }
4516 :
4517 0 : backoff = 1;
4518 :
4519 0 : size = sizeof(run_number);
4520 0 : status = db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4521 0 : assert(status == SUCCESS);
4522 :
4523 0 : if (run_number <= 0) {
4524 0 : cm_msg(MERROR, "start_the_run", "aborting on attempt to use invalid run number %d", run_number);
4525 0 : abort();
4526 : }
4527 :
4528 0 : int trans_flag = get_trans_flag();
4529 :
4530 0 : cm_msg(MTALK, "start_the_run", "starting new run");
4531 0 : status = cm_transition(TR_START, run_number + 1, errstr, sizeof(errstr), trans_flag, verbose);
4532 0 : if (status != CM_SUCCESS)
4533 0 : cm_msg(MERROR, "start_the_run", "cannot restart run: cm_transition() status %d, error: %s", status, errstr);
4534 :
4535 0 : return status;
4536 : }
4537 :
4538 0 : INT log_write(LOG_CHN * log_chn, EVENT_HEADER * pevent)
4539 : {
4540 0 : INT status = 0, size;
4541 : DWORD duration;
4542 : BOOL next_subrun;
4543 :
4544 : //printf("log_write %d\n", pevent->data_size + sizeof(EVENT_HEADER));
4545 :
4546 0 : DWORD start_time = ss_millitime();
4547 0 : int evt_size = pevent->data_size + sizeof(EVENT_HEADER);
4548 :
4549 0 : if (log_chn->writer) {
4550 0 : WriterInterface* wr = log_chn->writer;
4551 0 : status = wr->wr_write(log_chn, pevent, evt_size);
4552 :
4553 0 : if (status == SUCCESS) {
4554 : /* update statistics */
4555 0 : log_chn->statistics.events_written++;
4556 0 : log_chn->statistics.bytes_written_uncompressed += evt_size;
4557 : }
4558 :
4559 0 : double incr = wr->fBytesOut - log_chn->statistics.bytes_written_subrun;
4560 0 : if (incr < 0)
4561 0 : incr = 0;
4562 :
4563 : //printf("events %.0f, bytes out %.0f, incr %.0f, subrun %.0f, written %.0f, total %.0f\n", log_chn->statistics.events_written, wr->fBytesOut, incr, log_chn->statistics.bytes_written_subrun, log_chn->statistics.bytes_written, log_chn->statistics.bytes_written_total);
4564 :
4565 0 : log_chn->statistics.bytes_written += incr;
4566 0 : log_chn->statistics.bytes_written_subrun = wr->fBytesOut;
4567 0 : log_chn->statistics.bytes_written_total += incr;
4568 : }
4569 : #ifdef OBSOLETE
4570 : } else {
4571 : int written = 0;
4572 :
4573 : if (log_chn->format == FORMAT_MIDAS) {
4574 : written = midas_write(log_chn, pevent, pevent->data_size + sizeof(EVENT_HEADER));
4575 : #ifdef HAVE_ROOT
4576 : } else if (log_chn->format == FORMAT_ROOT) {
4577 : written = root_write(log_chn, pevent, pevent->data_size + sizeof(EVENT_HEADER));
4578 : #endif
4579 : }
4580 :
4581 : /* update statistics */
4582 : if (written > 0) {
4583 : log_chn->statistics.events_written++;
4584 : log_chn->statistics.bytes_written_uncompressed += evt_size;
4585 : log_chn->statistics.bytes_written += written;
4586 : log_chn->statistics.bytes_written_total += written;
4587 : log_chn->statistics.bytes_written_subrun += written;
4588 : }
4589 :
4590 : if (written < 0)
4591 : status = SS_FILE_ERROR;
4592 : else
4593 : status = SS_SUCCESS;
4594 : }
4595 : #endif
4596 :
4597 0 : DWORD actual_time = ss_millitime();
4598 0 : if (actual_time - start_time > 3000)
4599 0 : cm_msg(MINFO, "log_write", "Write operation on \'%s\' took %d ms", log_chn->path.c_str(), actual_time - start_time);
4600 :
4601 0 : if (status != SS_SUCCESS && !stop_requested) {
4602 0 : cm_msg(MTALK, "log_write", "Error writing output file, stopping run");
4603 0 : cm_msg(MERROR, "log_write", "Cannot write \'%s\', error %d, stopping run", log_chn->path.c_str(), status);
4604 0 : stop_the_run(0);
4605 :
4606 0 : return status;
4607 : }
4608 :
4609 : /* check if event limit is reached to stop run */
4610 0 : if (!stop_requested && !in_stop_transition &&
4611 0 : log_chn->settings.event_limit > 0 &&
4612 0 : log_chn->statistics.events_written >= log_chn->settings.event_limit) {
4613 0 : stop_requested = TRUE;
4614 :
4615 0 : cm_msg(MTALK, "log_write", "stopping run after having received %1.0lf events",
4616 : log_chn->settings.event_limit);
4617 :
4618 0 : status = stop_the_run(1);
4619 0 : return status;
4620 : }
4621 :
4622 : /* check if duration is reached for subrun */
4623 0 : duration = 0;
4624 0 : size = sizeof(duration);
4625 0 : db_get_value(hDB, 0, "/Logger/Subrun duration", &duration, &size, TID_UINT32, TRUE);
4626 0 : if (!stop_requested && duration > 0 && ss_time() >= subrun_start_time + duration) {
4627 : int run_number;
4628 :
4629 : // cm_msg(MTALK, "main", "stopping subrun after %d seconds", duration);
4630 :
4631 0 : size = sizeof(run_number);
4632 0 : status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4633 0 : assert(status == SUCCESS);
4634 :
4635 0 : stop_requested = TRUE; // avoid recursive call thourgh log_odb_dump
4636 0 : log_close(log_chn, run_number);
4637 0 : log_chn->subrun_number++;
4638 0 : log_chn->statistics.bytes_written_subrun = 0;
4639 0 : log_create_writer(log_chn);
4640 0 : log_generate_file_name(log_chn);
4641 0 : log_open(log_chn, run_number);
4642 0 : subrun_start_time = ss_time();
4643 0 : stop_requested = FALSE;
4644 : }
4645 :
4646 : /* check if byte limit is reached for subrun */
4647 0 : if (!stop_requested && log_chn->settings.subrun_byte_limit > 0 &&
4648 0 : log_chn->statistics.bytes_written_subrun >= log_chn->settings.subrun_byte_limit) {
4649 : int run_number;
4650 :
4651 : // cm_msg(MTALK, "main", "stopping subrun after %1.0lf bytes", log_chn->settings.subrun_byte_limit);
4652 :
4653 0 : size = sizeof(run_number);
4654 0 : status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4655 0 : assert(status == SUCCESS);
4656 :
4657 0 : stop_requested = TRUE; // avoid recursive call thourgh log_odb_dump
4658 0 : log_close(log_chn, run_number);
4659 0 : log_chn->subrun_number++;
4660 0 : log_chn->statistics.bytes_written_subrun = 0;
4661 0 : log_create_writer(log_chn);
4662 0 : log_generate_file_name(log_chn);
4663 0 : log_open(log_chn, run_number);
4664 0 : subrun_start_time = ss_time();
4665 0 : stop_requested = FALSE;
4666 : }
4667 :
4668 : /* check if new subrun is requested manually */
4669 0 : next_subrun = FALSE;
4670 0 : size = sizeof(next_subrun);
4671 0 : db_get_value(hDB, 0, "/Logger/Next subrun", &next_subrun, &size, TID_BOOL, true);
4672 0 : if (!stop_requested && next_subrun) {
4673 : int run_number;
4674 :
4675 : // cm_msg(MTALK, "main", "stopping subrun by user request");
4676 :
4677 0 : size = sizeof(run_number);
4678 0 : status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4679 0 : assert(status == SUCCESS);
4680 :
4681 0 : stop_requested = TRUE; // avoid recursive call thourgh log_odb_dump
4682 0 : log_close(log_chn, run_number);
4683 0 : log_chn->subrun_number++;
4684 0 : log_chn->statistics.bytes_written_subrun = 0;
4685 0 : log_create_writer(log_chn);
4686 0 : log_generate_file_name(log_chn);
4687 0 : log_open(log_chn, run_number);
4688 0 : subrun_start_time = ss_time();
4689 0 : stop_requested = FALSE;
4690 :
4691 0 : next_subrun = FALSE;
4692 0 : db_set_value(hDB, 0, "/Logger/Next subrun", &next_subrun, sizeof(next_subrun), 1, TID_BOOL);
4693 : }
4694 :
4695 : /* check if byte limit is reached to stop run */
4696 0 : if (!stop_requested && !in_stop_transition &&
4697 0 : log_chn->settings.byte_limit > 0 &&
4698 0 : log_chn->statistics.bytes_written >= log_chn->settings.byte_limit) {
4699 0 : stop_requested = TRUE;
4700 :
4701 0 : cm_msg(MTALK, "log_write", "stopping run after having received %1.0lf mega bytes",
4702 0 : log_chn->statistics.bytes_written / 1E6);
4703 :
4704 0 : status = stop_the_run(1);
4705 :
4706 0 : return status;
4707 : }
4708 :
4709 : /* stop run if less than 10MB free disk space */
4710 0 : actual_time = ss_millitime();
4711 0 : if (log_chn->type == LOG_TYPE_DISK && log_chn->do_disk_level && actual_time - log_chn->last_checked > DISK_CHECK_INTERVAL_MILLISEC) {
4712 0 : log_chn->last_checked = actual_time;
4713 :
4714 0 : const double MiB = 1024*1024;
4715 0 : double disk_size = 0;
4716 0 : double disk_free = 0;
4717 :
4718 0 : log_disk_level(log_chn, &disk_size, &disk_free);
4719 :
4720 0 : double limit = 10E6;
4721 :
4722 0 : if (disk_size > 100E9) {
4723 0 : limit = 1000E6;
4724 0 : } else if (disk_size > 10E9) {
4725 0 : limit = 100E6;
4726 : }
4727 :
4728 0 : if (disk_free < limit) {
4729 0 : stop_requested = TRUE;
4730 0 : cm_msg(MTALK, "log_write", "disk nearly full, stopping the run");
4731 0 : cm_msg(MERROR, "log_write", "Disk \'%s\' is almost full: %1.0lf MiBytes free out of %1.0f MiBytes, stopping the run", log_chn->path.c_str(), disk_free/MiB, disk_size/MiB);
4732 :
4733 0 : status = stop_the_run(0);
4734 : }
4735 : }
4736 :
4737 0 : return status;
4738 : }
4739 :
4740 : /*---- open_history ------------------------------------------------*/
4741 :
4742 : void log_history(HNDLE hDB, HNDLE hKey, void *info);
4743 :
4744 : #include "history.h"
4745 :
4746 : static std::vector<MidasHistoryInterface*> mh;
4747 : static std::vector<std::string> history_events;
4748 :
4749 0 : static int add_event(int* indexp, time_t timestamp, int event_id, const char* event_name, HNDLE hKey, int ntags, const TAG* tags, time_t min_period, int hotlink)
4750 : {
4751 : int status;
4752 : int size, i;
4753 0 : int index = *indexp;
4754 :
4755 : #if 0
4756 : {
4757 : /* print the tags */
4758 : printf("add_event: event %d, name \"%s\", ntags %d\n", event_id, event_name, ntags);
4759 : for (i=0; i<ntags; i++) {
4760 : printf("tag %d: name \"%s\", type %d, n_data %d\n", i, tags[i].name, tags[i].type, tags[i].n_data);
4761 : }
4762 : }
4763 : #endif
4764 :
4765 : /* check for duplicate event id's */
4766 0 : for (i=0; i<index; i++) {
4767 0 : if (strcmp(hist_log[i].event_name, event_name) == 0) {
4768 0 : cm_msg(MERROR, "add_event", "Duplicate event name \'%s\' with event id %d", event_name, event_id);
4769 0 : return 0;
4770 : }
4771 : }
4772 :
4773 0 : while (index >= hist_log_size) {
4774 0 : int new_size = 2*hist_log_size;
4775 :
4776 0 : if (hist_log_size == 0)
4777 0 : new_size = 10;
4778 :
4779 0 : hist_log = (hist_log_s*)realloc(hist_log, sizeof(hist_log[0])*new_size);
4780 0 : assert(hist_log!=NULL);
4781 :
4782 0 : hist_log_size = new_size;
4783 : }
4784 :
4785 0 : if (index >= hist_log_max)
4786 0 : hist_log_max = index + 1;
4787 :
4788 : /* check for invalid history tags */
4789 0 : for (i=0; i<ntags; i++) {
4790 0 : if (tags[i].type == TID_STRING) {
4791 0 : cm_msg(MERROR, "add_event", "Invalid tag %d \'%s\' in event %d \'%s\': cannot do history for TID_STRING data, sorry!", i, tags[i].name, event_id, event_name);
4792 0 : return 0;
4793 : }
4794 0 : if (tags[i].type == TID_INT64) {
4795 0 : cm_msg(MERROR, "add_event", "Invalid tag %d \'%s\' in event %d \'%s\': cannot do history for TID_INT64 data, sorry!", i, tags[i].name, event_id, event_name);
4796 0 : return 0;
4797 : }
4798 0 : if (tags[i].type == TID_UINT64) {
4799 0 : cm_msg(MERROR, "add_event", "Invalid tag %d \'%s\' in event %d \'%s\': cannot do history for TID_UINT64 data, sorry!", i, tags[i].name, event_id, event_name);
4800 0 : return 0;
4801 : }
4802 0 : if (rpc_tid_size(tags[i].type) == 0) {
4803 0 : cm_msg(MERROR, "add_event", "Invalid tag %d \'%s\' in event %d \'%s\': type %d size is zero", i, tags[i].name, event_id, event_name, tags[i].type);
4804 0 : return 0;
4805 : }
4806 : }
4807 :
4808 : /* check for trailing spaces in tag names */
4809 0 : for (i=0; i<ntags; i++) {
4810 0 : if (isspace(tags[i].name[strlen(tags[i].name)-1])) {
4811 0 : cm_msg(MERROR, "add_event", "Invalid tag %d \'%s\' in event %d \'%s\': has trailing spaces", i, tags[i].name, event_id, event_name);
4812 0 : return 0;
4813 : }
4814 : }
4815 :
4816 0 : for (unsigned i=0; i<mh.size(); i++) {
4817 0 : status = mh[i]->hs_define_event(event_name, timestamp, ntags, tags);
4818 0 : if (status != HS_SUCCESS) {
4819 0 : cm_msg(MERROR, "add_event", "Cannot define event \"%s\", hs_define_event() status %d", event_name, status);
4820 0 : return 0;
4821 : }
4822 : }
4823 :
4824 0 : status = db_get_record_size(hDB, hKey, 0, &size);
4825 :
4826 0 : if (status != DB_SUCCESS) {
4827 0 : cm_msg(MERROR, "add_event", "Cannot define event \"%s\", db_get_record_size() status %d", event_name, status);
4828 0 : return 0;
4829 : }
4830 :
4831 : /* setup hist_log structure for this event */
4832 0 : mstrlcpy(hist_log[index].event_name, event_name, sizeof(hist_log[index].event_name));
4833 0 : hist_log[index].n_var = ntags;
4834 0 : hist_log[index].hKeyVar = hKey;
4835 0 : hist_log[index].buffer_size = size;
4836 0 : hist_log[index].buffer = (char*)malloc(size);
4837 0 : hist_log[index].min_period = min_period;
4838 0 : hist_log[index].last_log = 0;
4839 :
4840 0 : if (hist_log[index].buffer == NULL) {
4841 0 : cm_msg(MERROR, "add_event", "Cannot allocate data buffer for event \"%s\" size %d", event_name, size);
4842 0 : return 0;
4843 : }
4844 :
4845 : /* open hot link to variables */
4846 0 : if (hotlink) {
4847 0 : status = db_open_record(hDB, hKey, hist_log[index].buffer, size, MODE_READ, log_history, NULL);
4848 0 : if (status != DB_SUCCESS) {
4849 0 : cm_msg(MERROR, "add_event",
4850 : "Cannot hotlink event %d \"%s\" for history logging, db_open_record() status %d",
4851 : event_id, event_name, status);
4852 0 : return status;
4853 : }
4854 : }
4855 :
4856 0 : history_events.push_back(event_name);
4857 :
4858 0 : if (verbose)
4859 0 : printf("Created event %d for equipment \"%s\", %d tags, size %d\n", event_id, event_name, ntags, size);
4860 :
4861 0 : *indexp = index+1;
4862 :
4863 0 : return SUCCESS;
4864 : }
4865 :
4866 0 : INT open_history()
4867 : {
4868 : INT size, index, i_tag, status, i, j, li, max_event_id;
4869 : int ieq;
4870 0 : INT n_var, n_tags, n_names = 0;
4871 : HNDLE hKeyRoot, hKeyVar, hLinkKey, hVarKey, hKeyEq, hHistKey, hKey;
4872 : HNDLE hKeyHist;
4873 0 : TAG *tag = NULL;
4874 : KEY key, varkey, linkkey, histkey;
4875 : WORD eq_id;
4876 : char str[256], eq_name[NAME_LENGTH], hist_name[NAME_LENGTH];
4877 0 : int global_per_variable_history = 0;
4878 :
4879 0 : time_t now = time(NULL);
4880 :
4881 0 : double tstart = ss_time_sec();
4882 :
4883 : // delete old history channels
4884 :
4885 0 : for (unsigned i=0; i<mh.size(); i++)
4886 0 : delete mh[i];
4887 0 : mh.clear();
4888 :
4889 0 : history_events.clear();
4890 :
4891 : // create and initialize the history channels tree
4892 :
4893 0 : status = db_find_key(hDB, 0, "/Logger/History", &hKeyHist);
4894 :
4895 0 : if (status == DB_NO_KEY) {
4896 : int active;
4897 0 : std::string type;
4898 : int debug;
4899 :
4900 : // create entry for FILE history
4901 :
4902 0 : type = "FILE";
4903 0 : status = db_get_value_string(hDB, 0, "/Logger/History/FILE/Type", 0, &type, TRUE);
4904 0 : assert(status==DB_SUCCESS);
4905 :
4906 0 : active = 1;
4907 0 : size = sizeof(active);
4908 0 : status = db_get_value(hDB, 0, "/Logger/History/FILE/Active", &active, &size, TID_BOOL, TRUE);
4909 0 : assert(status==DB_SUCCESS);
4910 :
4911 0 : debug = 0;
4912 0 : size = sizeof(debug);
4913 0 : status = db_get_value(hDB, 0, "/Logger/History/FILE/Debug", &debug, &size, TID_INT32, TRUE);
4914 0 : assert(status==DB_SUCCESS);
4915 :
4916 0 : int per_variable = 1;
4917 0 : size = sizeof(per_variable);
4918 0 : status = db_get_value(hDB, 0, "/Logger/History/FILE/PerVariableHistory", &per_variable, &size, TID_INT32, TRUE);
4919 0 : assert(status==DB_SUCCESS);
4920 :
4921 : // create entry for the MIDAS history
4922 :
4923 0 : type = "MIDAS";
4924 0 : status = db_get_value_string(hDB, 0, "/Logger/History/MIDAS/Type", 0, &type, TRUE);
4925 0 : assert(status==DB_SUCCESS);
4926 :
4927 0 : active = 0;
4928 0 : size = sizeof(active);
4929 0 : status = db_get_value(hDB, 0, "/Logger/History/MIDAS/Active", &active, &size, TID_BOOL, TRUE);
4930 0 : assert(status==DB_SUCCESS);
4931 :
4932 0 : debug = 0;
4933 0 : size = sizeof(debug);
4934 0 : status = db_get_value(hDB, 0, "/Logger/History/MIDAS/Debug", &debug, &size, TID_INT32, TRUE);
4935 0 : assert(status==DB_SUCCESS);
4936 :
4937 : // create entry for ODBC (MySQL) history
4938 :
4939 0 : type = "ODBC";
4940 0 : status = db_get_value_string(hDB, 0, "/Logger/History/ODBC/Type", 0, &type, TRUE);
4941 0 : assert(status==DB_SUCCESS);
4942 :
4943 0 : active = 0;
4944 0 : size = sizeof(active);
4945 0 : status = db_get_value(hDB, 0, "/Logger/History/ODBC/Active", &active, &size, TID_BOOL, TRUE);
4946 0 : assert(status==DB_SUCCESS);
4947 :
4948 0 : debug = 0;
4949 0 : size = sizeof(debug);
4950 0 : status = db_get_value(hDB, 0, "/Logger/History/ODBC/Debug", &debug, &size, TID_INT32, TRUE);
4951 0 : assert(status==DB_SUCCESS);
4952 :
4953 : // create entry for SQLITE history
4954 :
4955 0 : type = "SQLITE";
4956 0 : status = db_get_value_string(hDB, 0, "/Logger/History/SQLITE/Type", 0, &type, TRUE);
4957 0 : assert(status==DB_SUCCESS);
4958 :
4959 0 : active = 0;
4960 0 : size = sizeof(active);
4961 0 : status = db_get_value(hDB, 0, "/Logger/History/SQLITE/Active", &active, &size, TID_BOOL, TRUE);
4962 0 : assert(status==DB_SUCCESS);
4963 :
4964 0 : debug = 0;
4965 0 : size = sizeof(debug);
4966 0 : status = db_get_value(hDB, 0, "/Logger/History/SQLITE/Debug", &debug, &size, TID_INT32, TRUE);
4967 0 : assert(status==DB_SUCCESS);
4968 :
4969 : // create entry for MYSQL history writer
4970 :
4971 0 : type = "MYSQL";
4972 0 : status = db_get_value_string(hDB, 0, "/Logger/History/MYSQL/Type", 0, &type, TRUE);
4973 0 : assert(status==DB_SUCCESS);
4974 :
4975 0 : active = 0;
4976 0 : size = sizeof(active);
4977 0 : status = db_get_value(hDB, 0, "/Logger/History/MYSQL/Active", &active, &size, TID_BOOL, TRUE);
4978 0 : assert(status==DB_SUCCESS);
4979 :
4980 0 : debug = 0;
4981 0 : size = sizeof(debug);
4982 0 : status = db_get_value(hDB, 0, "/Logger/History/MYSQL/Debug", &debug, &size, TID_INT32, TRUE);
4983 0 : assert(status==DB_SUCCESS);
4984 :
4985 : // create entry for PGSQL history writer
4986 :
4987 0 : type = "PGSQL";
4988 0 : status = db_get_value_string(hDB, 0, "/Logger/History/PGSQL/Type", 0, &type, TRUE);
4989 0 : assert(status==DB_SUCCESS);
4990 :
4991 0 : active = 0;
4992 0 : size = sizeof(active);
4993 0 : status = db_get_value(hDB, 0, "/Logger/History/PGSQL/Active", &active, &size, TID_BOOL, TRUE);
4994 0 : assert(status==DB_SUCCESS);
4995 :
4996 0 : debug = 0;
4997 0 : size = sizeof(debug);
4998 0 : status = db_get_value(hDB, 0, "/Logger/History/PGSQL/Debug", &debug, &size, TID_INT32, TRUE);
4999 0 : assert(status==DB_SUCCESS);
5000 :
5001 : // get newly created /Logger/History
5002 :
5003 0 : status = db_find_key(hDB, 0, "/Logger/History", &hKeyHist);
5004 0 : }
5005 :
5006 0 : if (status != DB_SUCCESS) {
5007 0 : cm_msg(MERROR, "open_history", "Something is wrong with /Logger/History, db_find_key() status %d", status);
5008 0 : return status;
5009 : }
5010 :
5011 : // loop over history channels
5012 :
5013 0 : for (int ichan = 0; ; ichan++) {
5014 0 : status = db_enum_key(hDB, hKeyHist, ichan, &hKey);
5015 0 : if (status != DB_SUCCESS)
5016 0 : break;
5017 :
5018 0 : MidasHistoryInterface* hi = NULL;
5019 :
5020 0 : status = hs_get_history(hDB, hKey, HS_GET_WRITER, verbose, &hi);
5021 :
5022 0 : if (status==HS_SUCCESS && hi) {
5023 0 : if (strcasecmp(hi->type, "MIDAS")==0) {
5024 0 : i = 0;
5025 0 : size = sizeof(i);
5026 0 : status = db_get_value(hDB, hKey, "PerVariableHistory", &i, &size, TID_INT32, TRUE);
5027 0 : assert(status==DB_SUCCESS);
5028 :
5029 0 : if (i)
5030 0 : global_per_variable_history = 1;
5031 0 : } else if (strcasecmp(hi->type, "FILE")==0) {
5032 0 : i = 0;
5033 0 : size = sizeof(i);
5034 0 : status = db_get_value(hDB, hKey, "PerVariableHistory", &i, &size, TID_INT32, TRUE);
5035 0 : assert(status==DB_SUCCESS);
5036 :
5037 0 : if (i)
5038 0 : global_per_variable_history = 1;
5039 0 : } else if (strcasecmp(hi->type, "ODBC")==0) {
5040 0 : global_per_variable_history = 1;
5041 0 : } else if (strcasecmp(hi->type, "SQLITE")==0) {
5042 0 : global_per_variable_history = 1;
5043 0 : } else if (strcasecmp(hi->type, "MYSQL")==0) {
5044 0 : global_per_variable_history = 1;
5045 0 : } else if (strcasecmp(hi->type, "PGSQL")==0) {
5046 0 : global_per_variable_history = 1;
5047 : }
5048 :
5049 0 : if (verbose)
5050 0 : cm_msg(MINFO, "open_history", "Writing history to channel \'%s\' type \'%s\'", hi->name, hi->type);
5051 :
5052 0 : mh.push_back(hi);
5053 : }
5054 0 : }
5055 :
5056 : // prepare history channels
5057 :
5058 0 : for (unsigned i=0; i<mh.size(); i++) {
5059 0 : status = mh[i]->hs_clear_cache();
5060 0 : assert(status == HS_SUCCESS);
5061 : }
5062 :
5063 : // check global per-variable history settings
5064 :
5065 0 : i = 0;
5066 0 : size = sizeof(i);
5067 0 : status = db_get_value(hDB, 0, "/History/PerVariableHistory", &i, &size, TID_INT32, FALSE);
5068 0 : if (status==DB_SUCCESS) {
5069 0 : cm_msg(MERROR, "open_history", "mlogger ODB setting /History/PerVariableHistory is obsolete, please delete it. Use /Logger/History/MIDAS/PerVariableHistory instead");
5070 0 : if (i)
5071 0 : global_per_variable_history = i;
5072 : }
5073 :
5074 0 : if (global_per_variable_history) {
5075 : static int previous = -1;
5076 0 : if (global_per_variable_history != previous) {
5077 0 : if (global_per_variable_history)
5078 0 : cm_msg(MINFO, "open_history", "Per-variable history is enabled");
5079 : else
5080 : ;//cm_msg(MINFO, "open_history", "Per-variable history is disabled");
5081 : }
5082 0 : previous = global_per_variable_history;
5083 : }
5084 :
5085 : // setup history links
5086 :
5087 0 : if (db_find_key(hDB, 0, "/History/Links", &hKeyRoot) != DB_SUCCESS ||
5088 0 : db_find_key(hDB, 0, "/History/Links/System", &hKeyRoot) != DB_SUCCESS) {
5089 : /* create default history keys */
5090 0 : db_create_key(hDB, 0, "/History/Links", TID_KEY);
5091 :
5092 0 : if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/Events per sec.", &hKeyEq) == DB_SUCCESS)
5093 0 : db_create_link(hDB, 0, "/History/Links/System/Trigger per sec.",
5094 : "/Equipment/Trigger/Statistics/Events per sec.");
5095 :
5096 0 : if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/kBytes per sec.", &hKeyEq) == DB_SUCCESS)
5097 0 : db_create_link(hDB, 0, "/History/Links/System/Trigger kB per sec.",
5098 : "/Equipment/Trigger/Statistics/kBytes per sec.");
5099 : }
5100 :
5101 : /*---- define equipment events as history ------------------------*/
5102 :
5103 0 : max_event_id = 0;
5104 :
5105 0 : status = db_find_key(hDB, 0, "/Equipment", &hKeyRoot);
5106 0 : if (status == DB_NO_KEY) {
5107 0 : cm_msg(MINFO, "open_history", "Cannot find /Equipment entry in database, history system is inactive");
5108 0 : return CM_SUCCESS;
5109 : }
5110 :
5111 0 : if (status != DB_SUCCESS) {
5112 0 : cm_msg(MERROR, "open_history", "Cannot find /Equipment entry in database, db_find_key() status %d", status);
5113 0 : return status;
5114 : }
5115 :
5116 : /* loop over equipment */
5117 0 : index = 0;
5118 0 : for (ieq = 0; ; ieq++) {
5119 0 : status = db_enum_key(hDB, hKeyRoot, ieq, &hKeyEq);
5120 0 : if (status != DB_SUCCESS)
5121 0 : break;
5122 :
5123 0 : int32_t min_period = 0; // in seconds
5124 :
5125 : /* retrieve min period for history logging */
5126 0 : size = sizeof(min_period);
5127 0 : db_get_value(hDB, hKeyEq, "Common/Log history", &min_period, &size, TID_INT32, TRUE);
5128 :
5129 : /* define history tags only if log history flag is on */
5130 0 : if (min_period > 0) {
5131 0 : BOOL per_variable_history = global_per_variable_history;
5132 :
5133 : /* get equipment name */
5134 0 : db_get_key(hDB, hKeyEq, &key);
5135 0 : strcpy(eq_name, key.name);
5136 :
5137 0 : if (strchr(eq_name, ':'))
5138 0 : cm_msg(MERROR, "open_history", "Equipment name \'%s\' contains characters \':\', this may break the history system", eq_name);
5139 :
5140 0 : status = db_find_key(hDB, hKeyEq, "Variables", &hKeyVar);
5141 0 : if (status != DB_SUCCESS) {
5142 0 : cm_msg(MERROR, "open_history", "Cannot find /Equipment/%s/Variables entry in database", eq_name);
5143 0 : return 0;
5144 : }
5145 :
5146 0 : size = sizeof(eq_id);
5147 0 : status = db_get_value(hDB, hKeyEq, "Common/Event ID", &eq_id, &size, TID_UINT16, TRUE);
5148 0 : assert(status == DB_SUCCESS);
5149 :
5150 0 : size = sizeof(int);
5151 0 : status = db_get_value(hDB, hKeyEq, "Settings/PerVariableHistory", &per_variable_history, &size, TID_INT32, FALSE);
5152 0 : assert(status == DB_SUCCESS || status == DB_NO_KEY);
5153 :
5154 0 : if (verbose)
5155 : printf
5156 0 : ("\n==================== Equipment \"%s\", ID %d =======================\n",
5157 : eq_name, eq_id);
5158 :
5159 : /* count keys in variables tree */
5160 0 : for (n_var = 0, n_tags = 0;; n_var++) {
5161 0 : status = db_enum_key(hDB, hKeyVar, n_var, &hKey);
5162 0 : if (status == DB_NO_MORE_SUBKEYS)
5163 0 : break;
5164 0 : db_get_key(hDB, hKey, &key);
5165 0 : if (key.type != TID_KEY) {
5166 0 : n_tags += key.num_values;
5167 : }
5168 : else {
5169 : int ii;
5170 0 : for (ii=0;; ii++) {
5171 : KEY vvarkey;
5172 : HNDLE hhKey;
5173 :
5174 0 : status = db_enum_key(hDB, hKey, ii, &hhKey);
5175 0 : if (status == DB_NO_MORE_SUBKEYS)
5176 0 : break;
5177 :
5178 : /* get variable key */
5179 0 : db_get_key(hDB, hhKey, &vvarkey);
5180 :
5181 0 : n_tags += vvarkey.num_values;
5182 0 : }
5183 : }
5184 0 : }
5185 :
5186 0 : if (n_var == 0)
5187 0 : cm_msg(MINFO, "open_history", "Equipment \"%s\" history is enabled, but there are no Variables in ODB", eq_name);
5188 :
5189 : /* create tag array */
5190 0 : tag = (TAG *) calloc(sizeof(TAG), n_tags);
5191 :
5192 0 : i_tag = 0;
5193 0 : for (i=0; ; i++) {
5194 0 : status = db_enum_key(hDB, hKeyVar, i, &hKey);
5195 0 : if (status == DB_NO_MORE_SUBKEYS)
5196 0 : break;
5197 :
5198 : /* get variable key */
5199 0 : db_get_key(hDB, hKey, &varkey);
5200 :
5201 :
5202 0 : HNDLE hKeyNames = 0;
5203 0 : BOOL single_names = false;
5204 :
5205 : /* look for names */
5206 :
5207 0 : if (!hKeyNames) {
5208 0 : sprintf(str, "Settings/Names %s", varkey.name);
5209 0 : db_find_key(hDB, hKeyEq, str, &hKeyNames);
5210 0 : if (hKeyNames) {
5211 0 : if (verbose)
5212 0 : printf("Using \"/Equipment/%s/Settings/Names %s\" for variable \"%s\"\n", eq_name, varkey.name, varkey.name);
5213 :
5214 : /* define tags from names list */
5215 0 : db_get_key(hDB, hKeyNames, &key);
5216 0 : n_names = key.num_values;
5217 : }
5218 : }
5219 :
5220 0 : if (!hKeyNames) {
5221 0 : db_find_key(hDB, hKeyEq, "Settings/Names", &hKeyNames);
5222 0 : single_names = (hKeyNames > 0);
5223 :
5224 0 : if (hKeyNames) {
5225 0 : if (verbose)
5226 0 : printf("Using \"/Equipment/%s/Settings/Names\" for variable \"%s\"\n", eq_name, varkey.name);
5227 :
5228 : /* define tags from names list */
5229 0 : db_get_key(hDB, hKeyNames, &key);
5230 0 : n_names = key.num_values;
5231 : }
5232 : }
5233 :
5234 0 : if (hKeyNames && n_names < varkey.num_values) {
5235 0 : cm_msg(MERROR, "open_history",
5236 : "Array size mismatch: \"/Equipment/%s/Settings/%s\" has %d entries while \"/Equipment/%s/Variables/%s\" has %d entries",
5237 : eq_name, key.name, n_names,
5238 : eq_name, varkey.name, varkey.num_values);
5239 0 : free(tag);
5240 0 : return 0;
5241 : }
5242 :
5243 0 : if (hKeyNames) {
5244 : /* loop over array elements */
5245 0 : for (j = 0; j < varkey.num_values; j++) {
5246 : char xname[256];
5247 :
5248 0 : tag[i_tag].name[0] = 0;
5249 :
5250 : /* get name #j */
5251 0 : size = sizeof(xname);
5252 0 : status = db_get_data_index(hDB, hKeyNames, xname, &size, j, TID_STRING);
5253 0 : if (status == DB_SUCCESS)
5254 0 : mstrlcpy(tag[i_tag].name, xname, sizeof(tag[i_tag].name));
5255 :
5256 0 : if (strlen(tag[i_tag].name) < 1) {
5257 : char buf[256];
5258 0 : sprintf(buf, "%d", j);
5259 0 : mstrlcpy(tag[i_tag].name, varkey.name, NAME_LENGTH);
5260 0 : mstrlcat(tag[i_tag].name, "_", NAME_LENGTH);
5261 0 : mstrlcat(tag[i_tag].name, buf, NAME_LENGTH);
5262 : }
5263 :
5264 : /* append variable key name for single name array */
5265 0 : if (single_names) {
5266 0 : if (strlen(tag[i_tag].name) + 1 + strlen(varkey.name) >= NAME_LENGTH) {
5267 0 : cm_msg(MERROR, "open_history",
5268 0 : "Name for history entry \"%s %s\" too long", tag[i_tag].name, varkey.name);
5269 0 : free(tag);
5270 0 : return 0;
5271 : }
5272 0 : mstrlcat(tag[i_tag].name, " ", NAME_LENGTH);
5273 0 : mstrlcat(tag[i_tag].name, varkey.name, NAME_LENGTH);
5274 : }
5275 :
5276 0 : tag[i_tag].type = varkey.type;
5277 0 : tag[i_tag].n_data = 1;
5278 :
5279 0 : if (verbose)
5280 0 : printf("Defined tag %d, name \"%s\", type %d, num_values %d\n",
5281 0 : i_tag, tag[i_tag].name, tag[i_tag].type, tag[i_tag].n_data);
5282 :
5283 0 : i_tag++;
5284 : }
5285 0 : } else if (varkey.type == TID_KEY) {
5286 : int ii;
5287 0 : for (ii=0;; ii++) {
5288 : KEY vvarkey;
5289 : HNDLE hhKey;
5290 :
5291 0 : status = db_enum_key(hDB, hKey, ii, &hhKey);
5292 0 : if (status == DB_NO_MORE_SUBKEYS)
5293 0 : break;
5294 :
5295 : /* get variable key */
5296 0 : db_get_key(hDB, hhKey, &vvarkey);
5297 :
5298 0 : mstrlcpy(tag[i_tag].name, varkey.name, NAME_LENGTH);
5299 0 : mstrlcat(tag[i_tag].name, "_", NAME_LENGTH);
5300 0 : mstrlcat(tag[i_tag].name, vvarkey.name, NAME_LENGTH);
5301 0 : tag[i_tag].type = vvarkey.type;
5302 0 : tag[i_tag].n_data = vvarkey.num_values;
5303 :
5304 0 : if (verbose)
5305 0 : printf("Defined tag %d, name \"%s\", type %d, num_values %d\n", i_tag, tag[i_tag].name,
5306 0 : tag[i_tag].type, tag[i_tag].n_data);
5307 :
5308 0 : i_tag++;
5309 0 : }
5310 : } else {
5311 0 : mstrlcpy(tag[i_tag].name, varkey.name, NAME_LENGTH);
5312 0 : tag[i_tag].type = varkey.type;
5313 0 : tag[i_tag].n_data = varkey.num_values;
5314 :
5315 0 : if (verbose)
5316 0 : printf("Defined tag %d, name \"%s\", type %d, num_values %d\n", i_tag, tag[i_tag].name,
5317 0 : tag[i_tag].type, tag[i_tag].n_data);
5318 :
5319 0 : i_tag++;
5320 : }
5321 :
5322 0 : if (per_variable_history && i_tag>0) {
5323 0 : WORD event_id = 0;
5324 : char event_name[NAME_LENGTH];
5325 :
5326 0 : mstrlcpy(event_name, eq_name, NAME_LENGTH);
5327 0 : mstrlcat(event_name, "/", NAME_LENGTH);
5328 0 : mstrlcat(event_name, varkey.name, NAME_LENGTH);
5329 :
5330 0 : assert(i_tag <= n_tags);
5331 :
5332 0 : status = add_event(&index, now, event_id, event_name, hKey, i_tag, tag, min_period, 1);
5333 0 : if (status != DB_SUCCESS)
5334 0 : return status;
5335 :
5336 0 : i_tag = 0;
5337 : } /* if per-variable history */
5338 :
5339 0 : } /* loop over variables */
5340 :
5341 0 : if (!per_variable_history && i_tag>0) {
5342 0 : assert(i_tag <= n_tags);
5343 :
5344 0 : status = add_event(&index, now, eq_id, eq_name, hKeyVar, i_tag, tag, min_period, 1);
5345 0 : if (status != DB_SUCCESS)
5346 0 : return status;
5347 :
5348 : }
5349 :
5350 0 : if (tag) {
5351 0 : free(tag);
5352 0 : tag = NULL;
5353 : }
5354 :
5355 : /* remember maximum event id for later use with system events */
5356 0 : if (eq_id > max_event_id)
5357 0 : max_event_id = eq_id;
5358 : }
5359 0 : } /* loop over equipments */
5360 :
5361 : /*---- define linked trees --------------------------------------*/
5362 :
5363 : /* round up event id */
5364 0 : max_event_id = ((int) ((max_event_id + 1) / 10) + 1) * 10;
5365 :
5366 0 : status = db_find_key(hDB, 0, "/History/Links", &hKeyRoot);
5367 0 : if (status == DB_SUCCESS) {
5368 0 : for (li = 0;; li++) {
5369 0 : status = db_enum_link(hDB, hKeyRoot, li, &hHistKey);
5370 0 : if (status == DB_NO_MORE_SUBKEYS)
5371 0 : break;
5372 :
5373 0 : db_get_key(hDB, hHistKey, &histkey);
5374 0 : strcpy(hist_name, histkey.name);
5375 0 : db_enum_key(hDB, hKeyRoot, li, &hHistKey);
5376 :
5377 0 : db_get_key(hDB, hHistKey, &key);
5378 0 : if (key.type != TID_KEY) {
5379 0 : cm_msg(MERROR, "open_history", "Only subkeys allows in /History/Links, key \"%s\"", key.name);
5380 0 : continue;
5381 : }
5382 :
5383 0 : if (verbose)
5384 0 : printf("\n==================== History link \"%s\", ID %d =======================\n",
5385 : hist_name, max_event_id);
5386 :
5387 : /* count subkeys in link */
5388 0 : for (i = n_var = 0;; i++) {
5389 0 : status = db_enum_key(hDB, hHistKey, i, &hKey);
5390 0 : if (status == DB_NO_MORE_SUBKEYS)
5391 0 : break;
5392 :
5393 0 : if (status == DB_SUCCESS && db_get_key(hDB, hKey, &key) == DB_SUCCESS) {
5394 0 : if (key.type != TID_KEY)
5395 0 : n_var++;
5396 : } else {
5397 0 : db_enum_link(hDB, hHistKey, i, &hKey);
5398 0 : db_get_key(hDB, hKey, &key);
5399 0 : cm_msg(MERROR, "open_history",
5400 : "History link /History/Links/%s/%s is invalid", hist_name, key.name);
5401 0 : return 0;
5402 : }
5403 : }
5404 :
5405 0 : if (n_var == 0)
5406 0 : cm_msg(MERROR, "open_history", "History event %s has no variables in ODB", hist_name);
5407 : else {
5408 : /* create tag array */
5409 0 : tag = (TAG *) calloc(sizeof(TAG), n_var);
5410 :
5411 0 : assert(tag != NULL);
5412 :
5413 0 : for (i = 0, size = 0, n_var = 0;; i++) {
5414 0 : status = db_enum_link(hDB, hHistKey, i, &hLinkKey);
5415 0 : if (status == DB_NO_MORE_SUBKEYS)
5416 0 : break;
5417 :
5418 : /* get link key */
5419 0 : db_get_key(hDB, hLinkKey, &linkkey);
5420 :
5421 0 : if (linkkey.type == TID_KEY)
5422 0 : continue;
5423 :
5424 : /* get link target */
5425 0 : db_enum_key(hDB, hHistKey, i, &hVarKey);
5426 0 : if (db_get_key(hDB, hVarKey, &varkey) == DB_SUCCESS) {
5427 : /* hot-link individual values */
5428 0 : if (histkey.type == TID_KEY) {
5429 0 : db_close_record(hDB, hVarKey); // close previously opened record
5430 0 : db_open_record(hDB, hVarKey, NULL, varkey.total_size, MODE_READ, log_system_history,
5431 0 : (void *) (POINTER_T) index);
5432 : }
5433 :
5434 0 : strcpy(tag[n_var].name, linkkey.name);
5435 0 : tag[n_var].type = varkey.type;
5436 0 : tag[n_var].n_data = varkey.num_values;
5437 :
5438 0 : if (verbose)
5439 0 : printf("Defined tag \"%s\", type %d, num_values %d\n",
5440 0 : tag[n_var].name, tag[n_var].type, tag[n_var].n_data);
5441 :
5442 0 : size += varkey.total_size;
5443 0 : n_var++;
5444 : }
5445 : }
5446 :
5447 : /* hot-link whole subtree */
5448 0 : if (histkey.type == TID_LINK) {
5449 0 : db_close_record(hDB, hHistKey); // close previously opened record
5450 0 : db_open_record(hDB, hHistKey, NULL, size, MODE_READ, log_system_history, (void *) (POINTER_T) index);
5451 : }
5452 :
5453 0 : status = add_event(&index, now, max_event_id, hist_name, hHistKey, n_var, tag, 0, 0);
5454 0 : if (status != DB_SUCCESS)
5455 0 : return status;
5456 :
5457 0 : free(tag);
5458 0 : tag = NULL;
5459 :
5460 0 : max_event_id++;
5461 : }
5462 : }
5463 : }
5464 :
5465 : /*---- define run start/stop event ------------------------------*/
5466 :
5467 0 : tag = (TAG *) calloc(sizeof(TAG), 2);
5468 :
5469 0 : assert(tag != NULL);
5470 :
5471 0 : strcpy(tag[0].name, "State");
5472 0 : tag[0].type = TID_UINT32;
5473 0 : tag[0].n_data = 1;
5474 :
5475 0 : strcpy(tag[1].name, "Run number");
5476 0 : tag[1].type = TID_UINT32;
5477 0 : tag[1].n_data = 1;
5478 :
5479 0 : const char* event_name = "Run transitions";
5480 :
5481 0 : for (unsigned i=0; i<mh.size(); i++) {
5482 0 : status = mh[i]->hs_define_event(event_name, now, 2, tag);
5483 0 : if (status != HS_SUCCESS) {
5484 0 : cm_msg(MERROR, "add_event", "Cannot define event \"%s\", hs_define_event() status %d", event_name, status);
5485 0 : return 0;
5486 : }
5487 : }
5488 :
5489 0 : history_events.push_back(event_name);
5490 :
5491 0 : free(tag);
5492 0 : tag = NULL;
5493 :
5494 : /* outcommented not to produce a log entry on every run
5495 : cm_msg(MINFO, "open_history", "Configured history with %d events", count_events);
5496 : */
5497 :
5498 0 : status = hs_save_event_list(&history_events);
5499 0 : if (status != HS_SUCCESS)
5500 0 : return status;
5501 :
5502 0 : double tend = ss_time_sec();
5503 0 : double telapsed = tend - tstart;
5504 0 : if (telapsed > 10.0) {
5505 0 : cm_msg(MERROR, "open_history", "open_history() took %.3f seconds", telapsed);
5506 : }
5507 :
5508 0 : return CM_SUCCESS;
5509 : }
5510 :
5511 : /*---- periodically flush history buffers---------------------------*/
5512 :
5513 : time_t last_history_flush = 0;
5514 :
5515 0 : void maybe_flush_history(time_t now)
5516 : {
5517 0 : time_t flush_period_sec = 1; // flush once every 1 seconds
5518 :
5519 0 : if ((last_history_flush == 0) || (now >= last_history_flush + flush_period_sec)) {
5520 :
5521 0 : if (verbose)
5522 0 : printf("flush history buffers!\n");
5523 :
5524 0 : for (unsigned h = 0; h < mh.size(); h++)
5525 0 : mh[h]->hs_flush_buffers();
5526 :
5527 0 : last_history_flush = now;
5528 : }
5529 0 : }
5530 :
5531 : /*---- close_history -----------------------------------------------*/
5532 :
5533 0 : void close_history()
5534 : {
5535 : INT status;
5536 : HNDLE hKeyRoot;
5537 :
5538 : /* close system history */
5539 0 : status = db_find_key(hDB, 0, "/History/Links", &hKeyRoot);
5540 0 : if (status == DB_SUCCESS) {
5541 0 : for (int i = 0;; i++) {
5542 : HNDLE hKey;
5543 0 : status = db_enum_key(hDB, hKeyRoot, i, &hKey);
5544 0 : if (status == DB_NO_MORE_SUBKEYS)
5545 0 : break;
5546 0 : db_close_record(hDB, hKey);
5547 0 : }
5548 : }
5549 :
5550 : /* close event history */
5551 0 : for (int i = 0; i < hist_log_max; i++)
5552 0 : if (hist_log[i].hKeyVar) {
5553 0 : db_close_record(hDB, hist_log[i].hKeyVar);
5554 0 : hist_log[i].hKeyVar = 0;
5555 0 : if (hist_log[i].buffer)
5556 0 : free(hist_log[i].buffer);
5557 0 : hist_log[i].buffer = NULL;
5558 : }
5559 :
5560 0 : for (unsigned h=0; h<mh.size(); h++) {
5561 0 : mh[h]->hs_disconnect();
5562 0 : delete mh[h];
5563 0 : mh[h] = NULL;
5564 : }
5565 :
5566 0 : mh.clear();
5567 0 : }
5568 :
5569 : /*---- log_history -------------------------------------------------*/
5570 :
5571 0 : void log_history(HNDLE hDB, HNDLE hKey, void *info)
5572 : {
5573 : INT i, size, status;
5574 0 : DWORD start_millitime = ss_millitime();
5575 :
5576 0 : time_t now = time(NULL);
5577 :
5578 0 : for (i = 0; i < hist_log_max; i++)
5579 0 : if (hist_log[i].hKeyVar == hKey)
5580 0 : break;
5581 :
5582 0 : if (i == hist_log_max)
5583 0 : return;
5584 :
5585 : /* check if over minimum period */
5586 0 : if (now - hist_log[i].last_log < hist_log[i].min_period)
5587 0 : return;
5588 :
5589 : /* check if event size has changed */
5590 0 : db_get_record_size(hDB, hKey, 0, &size);
5591 0 : if (size != hist_log[i].buffer_size) {
5592 0 : close_history();
5593 0 : status = open_history();
5594 0 : if (status != CM_SUCCESS) {
5595 0 : printf("Error in history system, aborting.\n");
5596 0 : cm_disconnect_experiment();
5597 0 : exit(1);
5598 : }
5599 0 : return;
5600 : }
5601 :
5602 0 : hist_log[i].last_log = now;
5603 :
5604 0 : if (verbose)
5605 0 : printf("Log history event: \'%s\', timestamp %s, buffer %p, size %d\n", hist_log[i].event_name, TimeToString(hist_log[i].last_log).c_str(), hist_log[i].buffer, hist_log[i].buffer_size);
5606 :
5607 0 : for (unsigned h=0; h<mh.size(); h++) {
5608 0 : status = mh[h]->hs_write_event(hist_log[i].event_name, hist_log[i].last_log, hist_log[i].buffer_size, hist_log[i].buffer);
5609 0 : if (verbose)
5610 0 : if (status != HS_SUCCESS)
5611 0 : printf("write_history_event: \'%s\', channel \'%s\' hs_write_event() status %d\n", hist_log[i].event_name, mh[h]->name, status);
5612 : }
5613 :
5614 0 : maybe_flush_history(now);
5615 :
5616 0 : DWORD end_millitime = ss_millitime();
5617 0 : if (end_millitime - start_millitime > 3000)
5618 0 : cm_msg(MINFO, "log_history", "History write operation took %d ms", end_millitime - start_millitime);
5619 : }
5620 :
5621 : /*------------------------------------------------------------------*/
5622 :
5623 0 : void log_system_history(HNDLE hDB, HNDLE hKey, void *info)
5624 : {
5625 : INT size, total_size, status, index;
5626 : DWORD i;
5627 : KEY key;
5628 0 : DWORD start_millitime = ss_millitime();
5629 :
5630 0 : index = (INT) (POINTER_T) info;
5631 :
5632 0 : time_t now = time(NULL);
5633 :
5634 : /* check if over period */
5635 0 : if (now - hist_log[index].last_log < hist_log[index].min_period)
5636 0 : return;
5637 :
5638 0 : for (i = 0, total_size = 0;; i++) {
5639 0 : status = db_enum_key(hDB, hist_log[index].hKeyVar, i, &hKey);
5640 0 : if (status == DB_NO_MORE_SUBKEYS)
5641 0 : break;
5642 :
5643 0 : db_get_key(hDB, hKey, &key);
5644 0 : size = key.total_size;
5645 0 : db_get_data(hDB, hKey, (char *) hist_log[index].buffer + total_size, &size, key.type);
5646 0 : total_size += size;
5647 : }
5648 :
5649 0 : if (i != hist_log[index].n_var) {
5650 0 : close_history();
5651 0 : status = open_history();
5652 0 : if (status != CM_SUCCESS) {
5653 0 : printf("Error in history system, aborting.\n");
5654 0 : cm_disconnect_experiment();
5655 0 : exit(1);
5656 : }
5657 0 : return;
5658 : }
5659 :
5660 0 : hist_log[index].last_log = now;
5661 :
5662 0 : if (verbose)
5663 0 : printf("write history event: \'%s\', timestamp %s, buffer %p, size %d\n", hist_log[index].event_name, TimeToString(hist_log[index].last_log).c_str(), hist_log[index].buffer, hist_log[index].buffer_size);
5664 :
5665 0 : for (unsigned h=0; h<mh.size(); h++)
5666 0 : mh[h]->hs_write_event(hist_log[index].event_name, hist_log[index].last_log, total_size, hist_log[index].buffer);
5667 :
5668 0 : maybe_flush_history(now);
5669 :
5670 0 : DWORD end_millitime = ss_millitime();
5671 0 : if (end_millitime - start_millitime > 3000)
5672 0 : cm_msg(MINFO, "log_system_history", "History write operation took %d ms", end_millitime - start_millitime);
5673 : }
5674 :
5675 : /*------------------------------------------------------------------*/
5676 :
5677 0 : int log_generate_file_name(LOG_CHN *log_chn)
5678 : {
5679 0 : INT size, status, run_number = 0;
5680 0 : std::string path;
5681 0 : std::string data_dir;
5682 : CHN_SETTINGS *chn_settings;
5683 :
5684 0 : chn_settings = &log_chn->settings;
5685 0 : size = sizeof(run_number);
5686 0 : status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
5687 0 : assert(status == SUCCESS);
5688 :
5689 0 : std::string filename = chn_settings->filename;
5690 : /* Check if data stream are throw pipe command */
5691 0 : log_chn->pipe_command = "";
5692 0 : bool ispipe = false;
5693 0 : if (log_chn->type == LOG_TYPE_DISK) {
5694 0 : char* p = strchr(chn_settings->filename, '>');
5695 0 : if (chn_settings->filename[0] == '|' && p) {
5696 : /* skip second arrow in ">>" */
5697 0 : while (*p == '>')
5698 0 : p++;
5699 : /* skip spaces after '>' */
5700 0 : while (*p == ' ')
5701 0 : p++;
5702 0 : filename = p;
5703 0 : ispipe = true;
5704 : // pipe_command is contents of chn_settings->filename without the leading "|" and trailing ">"
5705 0 : log_chn->pipe_command = "";
5706 0 : const char*s = chn_settings->filename;
5707 : /* skip leading pipe */
5708 0 : if (*s == '|')
5709 0 : s++;
5710 : /* skip spaces */
5711 0 : while (*s == ' ')
5712 0 : s++;
5713 : /* copy up to ">" */
5714 0 : while (*s != '>') {
5715 0 : log_chn->pipe_command += *s++;
5716 : }
5717 : /* copy as many ">" as they put there */
5718 0 : while (*s == '>') {
5719 0 : log_chn->pipe_command += *s++;
5720 : }
5721 : }
5722 : }
5723 :
5724 0 : std::string str;
5725 :
5726 : /* if disk, precede filename with directory if not already there */
5727 0 : if (log_chn->type == LOG_TYPE_DISK && filename[0] != DIR_SEPARATOR) {
5728 0 : db_get_value_string(hDB, 0, "/Logger/Data Dir", 0, &data_dir, TRUE);
5729 0 : if (data_dir.empty()) {
5730 0 : data_dir = cm_get_path();
5731 : } else {
5732 0 : if (data_dir.back() != DIR_SEPARATOR)
5733 0 : data_dir += DIR_SEPARATOR_STR;
5734 : }
5735 0 : str = data_dir;
5736 :
5737 : /* append subdirectory if requested */
5738 0 : if (chn_settings->subdir_format[0]) {
5739 0 : ss_tzset(); // required for localtime_r()
5740 : time_t now;
5741 0 : time(&now);
5742 : struct tm tms;
5743 0 : localtime_r(&now, &tms);
5744 :
5745 : char dir[256];
5746 0 : strftime(dir, sizeof(dir), chn_settings->subdir_format, &tms);
5747 0 : str += dir;
5748 0 : str += DIR_SEPARATOR_STR;
5749 : }
5750 :
5751 : /* create directory if needed */
5752 : #ifdef OS_WINNT
5753 : status = mkdir(str.c_str());
5754 : #else
5755 0 : status = mkdir(str.c_str(), 0755);
5756 : #endif
5757 : #if defined(EEXIST)
5758 0 : if (status == -1 && errno != EEXIST)
5759 0 : cm_msg(MERROR, "log_generate_file_name", "Cannot create subdirectory \"%s\", mkdir() errno %d (%s)", str.c_str(), errno, strerror(errno));
5760 : #endif
5761 :
5762 0 : str += filename;
5763 : } else {
5764 0 : str = filename;
5765 : }
5766 :
5767 : /* check if two "%" are present in filename */
5768 0 : if (strchr(str.c_str(), '%')) {
5769 0 : if (strchr(strchr(str.c_str(), '%')+1, '%')) {
5770 : /* substitude first "%d" by current run number, second "%d" by subrun number */
5771 0 : path = msprintf(str.c_str(), run_number, log_chn->subrun_number);
5772 : } else {
5773 : /* substitue "%d" by current run number */
5774 0 : path = msprintf(str.c_str(), run_number);
5775 : }
5776 : } else {
5777 0 : path = str;
5778 : }
5779 :
5780 : /* add required file extension */
5781 0 : if (log_chn->writer) {
5782 0 : path += log_chn->writer->wr_get_file_ext();
5783 : }
5784 :
5785 0 : log_chn->path = path;
5786 :
5787 : /* write back current file name to ODB */
5788 0 : std::string tmpstr;
5789 0 : if (strncmp(path.c_str(), data_dir.c_str(), data_dir.length()) == 0)
5790 0 : tmpstr = path.c_str() + data_dir.length();
5791 : else
5792 0 : tmpstr = path;
5793 : char cstr[256];
5794 0 : mstrlcpy(cstr, tmpstr.c_str(), sizeof(cstr));
5795 0 : db_set_value(hDB, log_chn->settings_hkey, "Current filename", cstr, 256, 1, TID_STRING);
5796 :
5797 : /* construct full pipe command */
5798 0 : if (ispipe) {
5799 : /* check if %d must be substitude by current run number in pipe command options */
5800 0 : if (strchr(log_chn->pipe_command.c_str(), '%')) {
5801 0 : std::string str = log_chn->pipe_command;
5802 0 : if (strchr(strchr(str.c_str(), '%')+1, '%')) {
5803 : /* substitude first "%d" by current run number, second "%d" by subrun number */
5804 0 : log_chn->pipe_command = msprintf(str.c_str(), run_number, log_chn->subrun_number);
5805 : } else {
5806 : /* substitue "%d" by current run number */
5807 0 : log_chn->pipe_command = msprintf(str.c_str(), run_number);
5808 : }
5809 0 : } else {
5810 : }
5811 : /* add a space */
5812 0 : if (log_chn->pipe_command.back() != ' ')
5813 0 : log_chn->pipe_command += " ";
5814 : /* add generated filename to pipe command */
5815 0 : log_chn->pipe_command += path;
5816 : //printf("pipe command [%s]\n", log_chn->pipe_command.c_str());
5817 : }
5818 :
5819 0 : return CM_SUCCESS;
5820 0 : }
5821 :
5822 : /*------------------------------------------------------------------*/
5823 :
5824 : /********************************************************************\
5825 :
5826 : transition callbacks
5827 :
5828 : \********************************************************************/
5829 :
5830 : /*------------------------------------------------------------------*/
5831 :
5832 0 : int close_channels(int run_number, BOOL* p_tape_flag)
5833 : {
5834 0 : BOOL tape_flag = FALSE;
5835 :
5836 0 : for (unsigned i = 0; i < log_channels.size(); i++) {
5837 0 : LOG_CHN* chn = log_channels[i];
5838 0 : if (chn->handle || chn->ftp_con|| chn->pfile) {
5839 : /* generate MTALK message */
5840 : #ifndef FAL_MAIN
5841 : /* wait until buffer is empty */
5842 0 : if (chn->buffer_handle) {
5843 : #ifdef DELAYED_STOP
5844 : DWORD start_time = ss_millitime();
5845 : do {
5846 : cm_yield(100);
5847 : } while (ss_millitime() - start_time < DELAYED_STOP);
5848 : #else
5849 : INT n_bytes;
5850 : do {
5851 0 : bm_get_buffer_level(chn->buffer_handle, &n_bytes);
5852 0 : if (n_bytes > 0)
5853 0 : cm_yield(100);
5854 0 : } while (n_bytes > 0);
5855 : #endif
5856 : }
5857 : #endif /* FAL_MAIN */
5858 :
5859 : /* close logging channel */
5860 0 : log_close(chn, run_number);
5861 :
5862 : /* close statistics record */
5863 0 : db_set_record(hDB, chn->stats_hkey, &chn->statistics, sizeof(CHN_STATISTICS), 0);
5864 0 : db_close_record(hDB, chn->stats_hkey);
5865 0 : db_unwatch(hDB, chn->settings_hkey);
5866 0 : chn->stats_hkey = 0;
5867 0 : chn->settings_hkey = 0;
5868 : }
5869 : }
5870 :
5871 0 : if (p_tape_flag)
5872 0 : *p_tape_flag = tape_flag;
5873 :
5874 0 : return SUCCESS;
5875 : }
5876 :
5877 0 : int close_buffers()
5878 : {
5879 : /* close buffers */
5880 0 : for (unsigned i = 0; i < log_channels.size(); i++) {
5881 0 : LOG_CHN* chn = log_channels[i];
5882 : #ifndef FAL_MAIN
5883 0 : if (chn->buffer_handle) {
5884 0 : bm_close_buffer(chn->buffer_handle);
5885 0 : for (unsigned j = i + 1; j < log_channels.size(); j++)
5886 0 : if (log_channels[j]->buffer_handle == chn->buffer_handle)
5887 0 : log_channels[j]->buffer_handle = 0;
5888 : }
5889 :
5890 0 : if (chn->msg_request_id)
5891 0 : bm_delete_request(chn->msg_request_id);
5892 : #endif
5893 :
5894 0 : delete chn;
5895 0 : log_channels[i] = NULL;
5896 : }
5897 :
5898 0 : log_channels.clear();
5899 :
5900 0 : return SUCCESS;
5901 : }
5902 :
5903 : /*------------------------------------------------------------------*/
5904 :
5905 0 : static int write_history(DWORD transition, DWORD run_number)
5906 : {
5907 : DWORD eb[2];
5908 0 : eb[0] = transition;
5909 0 : eb[1] = run_number;
5910 :
5911 0 : time_t now = time(NULL);
5912 :
5913 0 : for (unsigned h=0; h<mh.size(); h++)
5914 0 : mh[h]->hs_write_event("Run transitions", now, sizeof(eb), (const char*)eb);
5915 :
5916 0 : return SUCCESS;
5917 : }
5918 :
5919 : /*------------------------------------------------------------------*/
5920 :
5921 0 : static void watch_settings(HNDLE hDB, HNDLE hKey, HNDLE index, void* info)
5922 : {
5923 : int status;
5924 0 : assert(info != NULL);
5925 0 : LOG_CHN *log_chn = (LOG_CHN*)info;
5926 0 : int size = sizeof(CHN_SETTINGS);
5927 0 : status = db_get_record1(hDB, log_chn->settings_hkey, &log_chn->settings, &size, 0, strcomb1(chn_settings_str).c_str());
5928 0 : if (status != DB_SUCCESS) {
5929 0 : cm_msg(MINFO, "watch_settings", "db_get_record(%s) status %d", log_chn->name.c_str(), status);
5930 0 : return;
5931 : }
5932 :
5933 0 : if (verbose)
5934 0 : printf("Channel %s settings updated\n", log_chn->name.c_str());
5935 : }
5936 :
5937 : /*------------------------------------------------------------------*/
5938 :
5939 0 : INT tr_start(INT run_number, char *error)
5940 : /********************************************************************\
5941 :
5942 : Prestart:
5943 :
5944 : Loop through channels defined in /logger/channels.
5945 : Neglect channels with are not active.
5946 : If "filename" contains a "%", substitute it by the
5947 : current run number. Open logging channel and
5948 : corresponding buffer. Place a event request
5949 : into the buffer.
5950 :
5951 : \********************************************************************/
5952 : {
5953 : INT size, status;
5954 : HNDLE hKeyRoot, hKeyChannel;
5955 : CHN_SETTINGS *chn_settings;
5956 : KEY key;
5957 0 : BOOL write_data, tape_flag = FALSE;
5958 : HNDLE hDB;
5959 :
5960 0 : if (verbose)
5961 0 : printf("tr_start: run %d\n", run_number);
5962 :
5963 0 : DWORD t0 = ss_millitime();
5964 :
5965 0 : cm_get_experiment_database(&hDB, NULL);
5966 :
5967 : /* save current ODB */
5968 0 : std::string str = "last.json";
5969 0 : db_get_value_string(hDB, 0, "/Logger/ODB Last Dump File", 0, &str, TRUE);
5970 0 : odb_save(str.c_str(), false);
5971 :
5972 0 : DWORD t1 = ss_millitime();
5973 :
5974 0 : in_stop_transition = TRUE;
5975 :
5976 0 : close_channels(run_number, NULL);
5977 0 : close_buffers();
5978 :
5979 0 : DWORD t2 = ss_millitime();
5980 :
5981 0 : in_stop_transition = FALSE;
5982 :
5983 0 : run_start_time = subrun_start_time = ss_time();
5984 :
5985 : /* read global logging flag */
5986 0 : size = sizeof(BOOL);
5987 0 : write_data = TRUE;
5988 0 : db_get_value(hDB, 0, "/Logger/Write data", &write_data, &size, TID_BOOL, TRUE);
5989 :
5990 : /* read tape message flag */
5991 0 : size = sizeof(tape_message);
5992 0 : db_get_value(hDB, 0, "/Logger/Tape message", &tape_message, &size, TID_BOOL, TRUE);
5993 :
5994 : /* reset next subrun flag */
5995 0 : status = FALSE;
5996 0 : db_set_value(hDB, 0, "/Logger/Next subrun", &status, sizeof(status), 1, TID_BOOL);
5997 :
5998 : /* loop over all channels */
5999 0 : status = db_find_key(hDB, 0, "/Logger/Channels", &hKeyRoot);
6000 0 : if (status != DB_SUCCESS) {
6001 : /* if no channels are defined, define at least one */
6002 0 : status = db_create_record(hDB, 0, "/Logger/Channels/0/", strcomb1(chn_tree_str).c_str());
6003 0 : if (status != DB_SUCCESS) {
6004 0 : strcpy(error, "Cannot create channel entry in database");
6005 0 : cm_msg(MERROR, "tr_start", "%s", error);
6006 0 : return 0;
6007 : }
6008 :
6009 0 : status = db_find_key(hDB, 0, "/Logger/Channels", &hKeyRoot);
6010 0 : if (status != DB_SUCCESS) {
6011 0 : strcpy(error, "Cannot create channel entry in database");
6012 0 : cm_msg(MERROR, "tr_start", "%s", error);
6013 0 : return 0;
6014 : }
6015 : }
6016 :
6017 : // after close_buffers() all log channels are closed and deleted
6018 0 : assert(log_channels.size() == 0);
6019 :
6020 0 : for (unsigned index = 0; ; index++) {
6021 0 : status = db_enum_key(hDB, hKeyRoot, index, &hKeyChannel);
6022 0 : if (status == DB_NO_MORE_SUBKEYS)
6023 0 : break;
6024 :
6025 : /* correct channel record */
6026 0 : db_get_key(hDB, hKeyChannel, &key);
6027 0 : status = db_check_record(hDB, hKeyRoot, key.name, strcomb1(chn_tree_str).c_str(), TRUE);
6028 0 : if (status != DB_SUCCESS && status != DB_OPEN_RECORD) {
6029 0 : cm_msg(MERROR, "tr_start", "Cannot create/check channel record, status %d", status);
6030 0 : break;
6031 : }
6032 :
6033 0 : if (status == DB_SUCCESS || status == DB_OPEN_RECORD) {
6034 0 : LOG_CHN* chn = new_LOG_CHN(key.name);
6035 :
6036 0 : log_channels.push_back(chn);
6037 :
6038 : /* save settings key */
6039 0 : status = db_find_key(hDB, hKeyChannel, "Settings", &chn->settings_hkey);
6040 0 : if (status != DB_SUCCESS) {
6041 0 : strcpy(error, "Cannot find channel settings info");
6042 0 : cm_msg(MERROR, "tr_start", "%s", error);
6043 0 : return 0;
6044 : }
6045 :
6046 : /* save statistics key */
6047 0 : status = db_find_key(hDB, hKeyChannel, "Statistics", &chn->stats_hkey);
6048 0 : if (status != DB_SUCCESS) {
6049 0 : strcpy(error, "Cannot find channel statistics info");
6050 0 : cm_msg(MERROR, "tr_start", "%s", error);
6051 0 : return 0;
6052 : }
6053 :
6054 : /* clear statistics */
6055 0 : size = sizeof(CHN_STATISTICS);
6056 0 : db_get_record1(hDB, chn->stats_hkey, &chn->statistics, &size, 0, strcomb1(chn_statistics_str).c_str());
6057 :
6058 0 : chn->statistics.events_written = 0;
6059 0 : chn->statistics.bytes_written = 0;
6060 0 : chn->statistics.bytes_written_uncompressed = 0;
6061 0 : chn->statistics.bytes_written_subrun = 0;
6062 :
6063 0 : db_set_record(hDB, chn->stats_hkey, &chn->statistics, size, 0);
6064 :
6065 : /* get channel info structure */
6066 0 : chn_settings = &chn->settings;
6067 0 : size = sizeof(CHN_SETTINGS);
6068 0 : status = db_get_record1(hDB, chn->settings_hkey, chn_settings, &size, 0, strcomb1(chn_settings_str).c_str());
6069 0 : if (status != DB_SUCCESS) {
6070 0 : strcpy(error, "Cannot read channel info");
6071 0 : cm_msg(MERROR, "tr_start", "%s", error);
6072 0 : return 0;
6073 : }
6074 :
6075 0 : chn->pre_checksum_module = select_checksum_module(hDB, chn->settings_hkey, "data checksum");
6076 0 : chn->post_checksum_module = select_checksum_module(hDB, chn->settings_hkey, "file checksum");
6077 0 : chn->compression_module = select_compression_module(hDB, chn->settings_hkey, "compress");
6078 0 : chn->output_module = select_output_module(hDB, chn->settings_hkey, "output");
6079 :
6080 : //printf("channel settings pre [%d] compress [%d] post [%d] output [%d]\n", chn->pre_checksum_module, chn->compression_module, chn->post_checksum_module, chn->output_module);
6081 :
6082 : /* check if active */
6083 0 : if (!chn_settings->active || !write_data)
6084 0 : continue;
6085 :
6086 : #ifdef OBSOLETE
6087 : /* check for type */
6088 : if (equal_ustring(chn_settings->type, "FTP"))
6089 : chn->type = LOG_TYPE_FTP;
6090 : else if (equal_ustring(chn_settings->type, "Disk"))
6091 : chn->type = LOG_TYPE_DISK;
6092 : else {
6093 : sprintf(error, "Invalid channel type \"%s\", pease use \"Tape\", \"FTP\" or \"Disk\"", chn_settings->type);
6094 : cm_msg(MERROR, "tr_start", "%s", error);
6095 : return 0;
6096 : }
6097 : #endif
6098 :
6099 : #ifdef OBSOLETE
6100 : /* set compression level */
6101 : chn->compression = 0;
6102 : size = sizeof(chn->compression);
6103 : status = db_get_value(hDB, chn->settings_hkey, "Compression", &chn->compression, &size, TID_INT32, FALSE);
6104 : #endif
6105 :
6106 : /* initialize subrun number */
6107 0 : chn->subrun_number = 0;
6108 :
6109 0 : log_create_writer(chn);
6110 0 : log_generate_file_name(chn);
6111 :
6112 : /* open logging channel */
6113 0 : status = log_open(chn, run_number);
6114 :
6115 : /* return if logging channel couldn't be opened */
6116 0 : if (status != SS_SUCCESS) {
6117 0 : if (status == SS_FILE_ERROR)
6118 0 : sprintf(error, "Cannot open file \'%s\' (See messages)", chn->path.c_str());
6119 0 : if (status == SS_FILE_EXISTS)
6120 0 : sprintf(error, "File \'%s\' exists already, run start aborted", chn->path.c_str());
6121 0 : if (status == SS_NO_TAPE)
6122 0 : sprintf(error, "No tape in device \'%s\'", chn->path.c_str());
6123 0 : if (status == SS_TAPE_ERROR)
6124 0 : sprintf(error, "Tape error, cannot start run");
6125 0 : if (status == SS_DEV_BUSY)
6126 0 : sprintf(error, "Device \'%s\' used by someone else", chn->path.c_str());
6127 0 : if (status == FTP_NET_ERROR || status == FTP_RESPONSE_ERROR)
6128 0 : sprintf(error, "Cannot open FTP channel to \'%s\'", chn->path.c_str());
6129 0 : if (status == SS_NO_ROOT)
6130 0 : sprintf(error, "No ROOT support compiled into mlogger, please compile with -DHAVE_ROOT flag");
6131 :
6132 0 : if (status == SS_INVALID_FORMAT)
6133 0 : sprintf(error, "Invalid data format, please use \"MIDAS\", \"ASCII\", \"DUMP\" or \"ROOT\"");
6134 :
6135 0 : cm_msg(MERROR, "tr_start", "%s", error);
6136 0 : return 0;
6137 : }
6138 :
6139 : /* close records if open from previous run start with abort */
6140 0 : if (chn->stats_hkey)
6141 0 : db_close_record(hDB, chn->stats_hkey);
6142 0 : if (chn->settings_hkey)
6143 0 : db_close_record(hDB, chn->settings_hkey);
6144 :
6145 : /* open hot link to statistics tree */
6146 0 : status = db_open_record1(hDB, chn->stats_hkey, &chn->statistics, sizeof(CHN_STATISTICS), MODE_WRITE, NULL, NULL, strcomb1(chn_statistics_str).c_str());
6147 0 : if (status == DB_NO_ACCESS) {
6148 : /* record is probably still in exclusive access by dead logger, so reset it */
6149 0 : status = db_set_mode(hDB, chn->stats_hkey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
6150 0 : if (status != DB_SUCCESS)
6151 0 : cm_msg(MERROR, "tr_start", "Cannot change access mode for statistics record, error %d", status);
6152 : else
6153 0 : cm_msg(MINFO, "tr_start", "Recovered access mode for statistics record of channel \"%s\"", chn->name.c_str());
6154 0 : status = db_open_record1(hDB, chn->stats_hkey, &chn->statistics, sizeof(CHN_STATISTICS), MODE_WRITE, NULL, NULL, strcomb1(chn_statistics_str).c_str());
6155 : }
6156 :
6157 0 : if (status != DB_SUCCESS)
6158 0 : cm_msg(MERROR, "tr_start", "Cannot open statistics record for channel \"%s\", error %d", chn->name.c_str(), status);
6159 :
6160 : /* open hot link to settings tree */
6161 0 : status = db_watch(hDB, chn->settings_hkey, watch_settings, chn);
6162 0 : if (status != DB_SUCCESS)
6163 0 : cm_msg(MERROR, "tr_start", "db_watch() status %d, cannot open channel settings record, probably other logger is using it", status);
6164 :
6165 : #ifndef FAL_MAIN
6166 : /* open buffer */
6167 0 : status = bm_open_buffer(chn_settings->buffer, DEFAULT_BUFFER_SIZE, &chn->buffer_handle);
6168 0 : if (status != BM_SUCCESS && status != BM_CREATED) {
6169 0 : sprintf(error, "Cannot open buffer %s", chn_settings->buffer);
6170 0 : cm_msg(MERROR, "tr_start", "%s", error);
6171 0 : return 0;
6172 : }
6173 0 : bm_set_cache_size(chn->buffer_handle, 1000000, 0);
6174 :
6175 : /* place event request */
6176 0 : status = bm_request_event(chn->buffer_handle, (short) chn_settings->event_id, (short) chn_settings->trigger_mask, GET_ALL, &chn->request_id, receive_event);
6177 :
6178 0 : if (status != BM_SUCCESS) {
6179 0 : sprintf(error, "Cannot place event request");
6180 0 : cm_msg(MERROR, "tr_start", "%s", error);
6181 0 : return 0;
6182 : }
6183 :
6184 : /* open message buffer if requested */
6185 0 : if (chn_settings->log_messages) {
6186 0 : status = bm_open_buffer((char*)MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &chn->msg_buffer_handle);
6187 0 : if (status != BM_SUCCESS && status != BM_CREATED) {
6188 0 : sprintf(error, "Cannot open buffer %s", MESSAGE_BUFFER_NAME);
6189 0 : cm_msg(MERROR, "tr_start", "%s", error);
6190 0 : return 0;
6191 : }
6192 :
6193 : /* place event request */
6194 0 : status = bm_request_event(chn->msg_buffer_handle, (short) EVENTID_MESSAGE, (short) chn_settings->log_messages, GET_ALL, &chn->msg_request_id, receive_event);
6195 :
6196 0 : if (status != BM_SUCCESS) {
6197 0 : sprintf(error, "Cannot place event request");
6198 0 : cm_msg(MERROR, "tr_start", "%s", error);
6199 0 : return 0;
6200 : }
6201 : }
6202 : #endif
6203 : }
6204 0 : }
6205 :
6206 0 : DWORD t3 = ss_millitime();
6207 :
6208 0 : if (tape_flag && tape_message)
6209 0 : cm_msg(MTALK, "tr_start", "tape mounting finished");
6210 :
6211 : /* write transition event into history */
6212 0 : write_history(STATE_RUNNING, run_number);
6213 :
6214 0 : DWORD t4 = ss_millitime();
6215 :
6216 : #ifdef HAVE_MYSQL
6217 : /* write to SQL database if requested */
6218 0 : write_runlog_sql(TRUE);
6219 : #endif
6220 :
6221 : /* write to runlog file(s) if requested */
6222 0 : write_runlog_ascii(TRUE);
6223 0 : write_runlog_json(TRUE);
6224 :
6225 0 : DWORD t5 = ss_millitime();
6226 0 : DWORD te = t5;
6227 :
6228 0 : if (verbose)
6229 0 : printf("tr_start: run %d started in %d ms: odb dump %d ms, close channels %d ms, configure channels %d ms, configure history %d ms, write runlog %d ms\n",
6230 : run_number,
6231 : te-t0,
6232 : t1-t0,
6233 : t2-t1,
6234 : t3-t2,
6235 : t4-t3,
6236 : t5-t4
6237 : );
6238 :
6239 0 : local_state = STATE_RUNNING;
6240 0 : run_start_time = subrun_start_time = ss_time();
6241 :
6242 0 : cm_msg_flush_buffer();
6243 :
6244 0 : return CM_SUCCESS;
6245 0 : }
6246 :
6247 : /*-- -------- ------------------------------------------------------*/
6248 :
6249 0 : INT tr_start_abort(INT run_number, char *error)
6250 : {
6251 0 : if (verbose)
6252 0 : printf("tr_start_abort: run %d\n", run_number);
6253 :
6254 0 : in_stop_transition = TRUE;
6255 :
6256 0 : for (unsigned i = 0; i < log_channels.size(); i++) {
6257 0 : LOG_CHN* chn = log_channels[i];
6258 0 : if (chn->handle && chn->type == LOG_TYPE_DISK) {
6259 0 : cm_msg(MINFO, "tr_start_abort", "Deleting previous file \"%s\"", chn->path.c_str());
6260 0 : unlink(chn->path.c_str());
6261 : }
6262 : }
6263 :
6264 0 : close_channels(run_number, NULL);
6265 0 : close_buffers();
6266 :
6267 0 : in_stop_transition = FALSE;
6268 :
6269 0 : local_state = STATE_STOPPED;
6270 :
6271 0 : cm_msg_flush_buffer();
6272 :
6273 0 : return CM_SUCCESS;
6274 : }
6275 :
6276 : /*-- poststop ------------------------------------------------------*/
6277 :
6278 0 : INT tr_stop(INT run_number, char *error)
6279 : /********************************************************************\
6280 :
6281 : Poststop:
6282 :
6283 : Wait until buffers are empty, then close logging channels
6284 :
6285 : \********************************************************************/
6286 : {
6287 : INT size;
6288 0 : BOOL flag, tape_flag = FALSE;
6289 : char filename[256];
6290 : char str[256];
6291 :
6292 0 : if (verbose)
6293 0 : printf("tr_stop: run %d\n", run_number);
6294 :
6295 0 : if (in_stop_transition)
6296 0 : return CM_SUCCESS;
6297 :
6298 0 : DWORD t0 = ss_millitime();
6299 :
6300 0 : in_stop_transition = TRUE;
6301 :
6302 0 : close_channels(run_number, &tape_flag);
6303 0 : close_buffers();
6304 :
6305 0 : DWORD t1 = ss_millitime();
6306 :
6307 : /* ODB dump if requested */
6308 0 : size = sizeof(flag);
6309 0 : flag = 0;
6310 0 : db_get_value(hDB, 0, "/Logger/ODB Dump", &flag, &size, TID_BOOL, TRUE);
6311 0 : if (flag) {
6312 0 : strcpy(str, "run%d.json");
6313 0 : size = sizeof(str);
6314 0 : str[0] = 0;
6315 0 : db_get_value(hDB, 0, "/Logger/ODB Dump File", str, &size, TID_STRING, TRUE);
6316 0 : if (str[0] == 0)
6317 0 : strcpy(str, "run%d.json");
6318 :
6319 : /* substitue "%d" by current run number */
6320 0 : if (strchr(str, '%'))
6321 0 : sprintf(filename, str, run_number);
6322 : else
6323 0 : strcpy(filename, str);
6324 :
6325 0 : odb_save(filename, true);
6326 : }
6327 :
6328 0 : DWORD t2 = ss_millitime();
6329 :
6330 : #ifdef HAVE_MYSQL
6331 : /* write to SQL database if requested */
6332 0 : write_runlog_sql(FALSE);
6333 : #endif
6334 :
6335 : /* write to ASCII file(s) if requested */
6336 0 : write_runlog_ascii(FALSE);
6337 0 : write_runlog_json(FALSE);
6338 :
6339 0 : DWORD t3 = ss_millitime();
6340 :
6341 0 : in_stop_transition = FALSE;
6342 :
6343 0 : if (tape_flag & tape_message)
6344 0 : cm_msg(MTALK, "tr_stop", "all tape channels closed");
6345 :
6346 : /* write transition event into history */
6347 0 : write_history(STATE_STOPPED, run_number);
6348 :
6349 0 : DWORD t4 = ss_millitime();
6350 :
6351 : /* clear flag */
6352 0 : stop_requested = FALSE;
6353 0 : stop_try_later = 0;
6354 :
6355 0 : if (start_requested) {
6356 0 : int delay = 0;
6357 0 : size = sizeof(delay);
6358 0 : db_get_value(hDB, 0, "/Logger/Auto restart delay", &delay, &size, TID_INT32, TRUE);
6359 0 : auto_restart = ss_time() + delay; /* start after specified delay */
6360 0 : start_requested = FALSE;
6361 : }
6362 :
6363 0 : DWORD te = t4;
6364 :
6365 0 : if (verbose)
6366 0 : printf("tr_stop: run %d stopped in %d ms: close channels %d ms, odb dump %d ms, write run log %d ms, write history %d ms\n",
6367 : run_number,
6368 : te-t0,
6369 : t1-t0,
6370 : t2-t1,
6371 : t3-t2,
6372 : t4-t3);
6373 :
6374 :
6375 0 : local_state = STATE_STOPPED;
6376 :
6377 0 : cm_msg_flush_buffer();
6378 :
6379 0 : return CM_SUCCESS;
6380 : }
6381 :
6382 : /*== common code FAL/MLOGGER end ===================================*/
6383 :
6384 : /*----- pause/resume -----------------------------------------------*/
6385 :
6386 0 : INT tr_pause(INT run_number, char *error)
6387 : {
6388 : /* write transition event into history */
6389 0 : write_history(STATE_PAUSED, run_number);
6390 :
6391 0 : local_state = STATE_PAUSED;
6392 :
6393 0 : cm_msg_flush_buffer();
6394 :
6395 0 : return CM_SUCCESS;
6396 : }
6397 :
6398 0 : INT tr_resume(INT run_number, char *error)
6399 : {
6400 : /* write transition event into history */
6401 0 : write_history(STATE_RUNNING, run_number);
6402 :
6403 0 : local_state = STATE_RUNNING;
6404 :
6405 0 : cm_msg_flush_buffer();
6406 :
6407 0 : return CM_SUCCESS;
6408 : }
6409 :
6410 : /*----- receive_event ----------------------------------------------*/
6411 :
6412 0 : void receive_event(HNDLE hBuf, HNDLE request_id, EVENT_HEADER * pheader, void *pevent)
6413 : {
6414 0 : if (verbose)
6415 0 : printf("write data event: req %d, evid %d, timestamp %d, size %d\n", request_id, pheader->event_id, pheader->time_stamp, pheader->data_size);
6416 :
6417 : /* find logging channel for this request id */
6418 0 : for (unsigned i = 0; i < log_channels.size(); i++) {
6419 0 : LOG_CHN* chn = log_channels[i];
6420 0 : if (chn->handle == 0 && chn->ftp_con == NULL)
6421 0 : continue;
6422 :
6423 : /* write normal events */
6424 0 : if (chn->request_id == request_id) {
6425 0 : log_write(chn, pheader);
6426 0 : break;
6427 : }
6428 :
6429 : /* write messages */
6430 0 : if (chn->msg_request_id == request_id) {
6431 0 : log_write(chn, pheader);
6432 0 : break;
6433 : }
6434 : }
6435 0 : }
6436 :
6437 : /*------------------------ main ------------------------------------*/
6438 :
6439 0 : int main(int argc, char *argv[])
6440 : {
6441 0 : INT status, msg, i, size, ch = 0;
6442 : char host_name[HOST_NAME_LENGTH], exp_name[NAME_LENGTH];
6443 : BOOL debug, daemon, save_mode;
6444 0 : DWORD last_time_kb = 0;
6445 0 : DWORD last_time_stat = 0;
6446 : DWORD duration;
6447 :
6448 : #ifdef HAVE_ROOT
6449 : char **rargv;
6450 : int rargc;
6451 :
6452 : /* copy first argument */
6453 : rargc = 0;
6454 : rargv = (char **) malloc(sizeof(char *) * 2);
6455 : assert(rargv != NULL);
6456 : rargv[rargc] = (char *) malloc(strlen(argv[rargc]) + 1);
6457 : assert(rargv[rargc] != NULL);
6458 : strcpy(rargv[rargc], argv[rargc]);
6459 : rargc++;
6460 :
6461 : /* append argument "-b" for batch mode without graphics */
6462 : rargv[rargc++] = (char *)"-b";
6463 :
6464 : TApplication theApp("mlogger", &rargc, rargv);
6465 :
6466 : /* free argument memory */
6467 : free(rargv[0]);
6468 : rargv[0] = NULL;
6469 : free(rargv);
6470 : rargv = NULL;
6471 :
6472 : #endif
6473 :
6474 : #ifdef SIGPIPE
6475 : // undo ROOT overwrites SIGPIPE
6476 0 : signal(SIGPIPE, SIG_IGN);
6477 : #endif
6478 :
6479 0 : setbuf(stdout, NULL);
6480 0 : setbuf(stderr, NULL);
6481 :
6482 : /* get default from environment */
6483 0 : cm_get_environment(host_name, sizeof(host_name), exp_name, sizeof(exp_name));
6484 :
6485 0 : debug = daemon = save_mode = FALSE;
6486 :
6487 : /* parse command line parameters */
6488 0 : for (i = 1; i < argc; i++) {
6489 0 : if (argv[i][0] == '-' && argv[i][1] == 'd')
6490 0 : debug = TRUE;
6491 0 : else if (argv[i][0] == '-' && argv[i][1] == 'D')
6492 0 : daemon = TRUE;
6493 0 : else if (argv[i][0] == '-' && argv[i][1] == 's')
6494 0 : save_mode = TRUE;
6495 0 : else if (argv[i][0] == '-' && argv[i][1] == 'v')
6496 0 : verbose = TRUE;
6497 0 : else if (argv[i][0] == '-') {
6498 0 : if (i + 1 >= argc || argv[i + 1][0] == '-')
6499 0 : goto usage;
6500 0 : if (argv[i][1] == 'e')
6501 0 : strcpy(exp_name, argv[++i]);
6502 : else {
6503 0 : usage:
6504 0 : printf("usage: mlogger [-e Experiment] [-d] [-D] [-s] [-v]\n\n");
6505 0 : return 1;
6506 : }
6507 : }
6508 : }
6509 :
6510 0 : if (daemon) {
6511 0 : printf("Becoming a daemon...\n");
6512 0 : ss_daemon_init(FALSE);
6513 : }
6514 :
6515 0 : status = cm_connect_experiment(host_name, exp_name, "Logger", NULL);
6516 0 : if (status != CM_SUCCESS)
6517 0 : return 1;
6518 :
6519 : /* check if logger already running */
6520 0 : status = cm_exist("Logger", FALSE);
6521 0 : if (status == CM_SUCCESS) {
6522 0 : printf("Logger runs already.\n");
6523 0 : cm_disconnect_experiment();
6524 0 : return 1;
6525 : }
6526 :
6527 0 : cm_get_experiment_database(&hDB, NULL);
6528 :
6529 : /* turn off watchdog if in debug mode */
6530 0 : if (debug)
6531 0 : cm_set_watchdog_params(TRUE, 0);
6532 :
6533 : /* turn on save mode */
6534 0 : if (save_mode) {
6535 0 : cm_set_watchdog_params(FALSE, 0);
6536 0 : db_protect_database(hDB);
6537 : }
6538 :
6539 : /* register transition callbacks */
6540 0 : if (cm_register_transition(TR_START, tr_start, 200) != CM_SUCCESS) {
6541 0 : cm_msg(MERROR, "main", "cannot register callbacks");
6542 0 : return 1;
6543 : }
6544 :
6545 0 : cm_register_transition(TR_STARTABORT, tr_start_abort, 800);
6546 0 : cm_register_transition(TR_STOP, tr_stop, 800);
6547 0 : cm_register_transition(TR_PAUSE, tr_pause, 800);
6548 0 : cm_register_transition(TR_RESUME, tr_resume, 200);
6549 :
6550 : /* initialize ODB */
6551 0 : logger_init();
6552 :
6553 : /* obtain current state */
6554 0 : local_state = STATE_STOPPED;
6555 0 : size = sizeof(local_state);
6556 0 : status = db_get_value(hDB, 0, "/Runinfo/State", &local_state, &size, TID_INT32, true);
6557 :
6558 : /* open history logging */
6559 0 : if (open_history() != CM_SUCCESS) {
6560 0 : printf("Error in history system, aborting startup.\n");
6561 0 : cm_disconnect_experiment();
6562 0 : return 1;
6563 : }
6564 :
6565 : /* print startup message */
6566 0 : std::string data_dir;
6567 0 : db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &data_dir, TRUE);
6568 0 : if (data_dir.length() <= 0)
6569 0 : data_dir = cm_get_path();
6570 0 : printf("Data directory is \"%s\" unless specified under /Logger/channels/\n", data_dir.c_str());
6571 :
6572 : /* Alternate message path */
6573 0 : std::string message_dir;
6574 0 : db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
6575 0 : if (message_dir.empty()) {
6576 0 : message_dir = data_dir;
6577 0 : printf("Message directory is \"%s\" unless specified in /Logger/Message dir\n", message_dir.c_str());
6578 : } else
6579 0 : printf("Message directory is \"%s\"\n", message_dir.c_str());
6580 :
6581 : /* Alternate History and Elog path */
6582 0 : std::string history_dir;
6583 0 : db_get_value_string(hDB, 0, "/Logger/History dir", 0, &history_dir, TRUE);
6584 0 : if (history_dir.empty()) {
6585 0 : history_dir = data_dir;
6586 0 : printf("History directory is \"%s\" unless specified under /Logger/history/\n", history_dir.c_str());
6587 : } else
6588 0 : printf("History directory is \"%s\"\n", history_dir.c_str());
6589 :
6590 : #ifdef HAVE_MYSQL
6591 : {
6592 0 : std::string sql_host;
6593 0 : std::string sql_db;
6594 0 : std::string sql_table;
6595 :
6596 : HNDLE hktemp;
6597 0 : status = db_find_key(hDB, 0, "/Logger/Runlog/SQL/Hostname", &hktemp);
6598 0 : if (status == DB_SUCCESS) {
6599 0 : db_get_value_string(hDB, 0, "/Logger/Runlog/SQL/Hostname", 0, &sql_host, FALSE);
6600 0 : db_get_value_string(hDB, 0, "/Logger/Runlog/SQL/Database", 0, &sql_db, FALSE);
6601 0 : db_get_value_string(hDB, 0, "/Logger/Runlog/SQL/Table", 0, &sql_table, FALSE);
6602 0 : printf("SQL database is %s/%s/%s", sql_host.c_str(), sql_db.c_str(), sql_table.c_str());
6603 : }
6604 0 : }
6605 : #endif
6606 :
6607 0 : printf("\nMIDAS logger started. Stop with \"!\"\n");
6608 :
6609 : /* initialize ss_getchar() */
6610 0 : ss_getchar(0);
6611 :
6612 : /* start image history threads */
6613 0 : start_image_history();
6614 :
6615 : do {
6616 0 : msg = cm_yield(100);
6617 :
6618 : /* maybe update channel disk levels */
6619 0 : maybe_check_disk_level();
6620 :
6621 : /* update channel statistics once every second */
6622 0 : if (ss_millitime() - last_time_stat > 1000) {
6623 0 : last_time_stat = ss_millitime();
6624 : /*
6625 : printf("update statistics!\n");
6626 : //LOG_CHN* log_chn = log_chn[0];
6627 : printf("events %.0f, subrun %.0f, written %.0f, total %.0f\n", log_chn->statistics.events_written,
6628 : log_chn->statistics.bytes_written_subrun,
6629 : log_chn->statistics.bytes_written,
6630 : log_chn->statistics.bytes_written_total);
6631 : */
6632 0 : db_send_changed_records();
6633 : }
6634 :
6635 : /* check for auto restart */
6636 0 : if (auto_restart && ss_time() > auto_restart) {
6637 0 : status = start_the_run();
6638 : }
6639 :
6640 : /* check if time is reached to stop run */
6641 0 : duration = 0;
6642 0 : size = sizeof(duration);
6643 0 : db_get_value(hDB, 0, "/Logger/Run duration", &duration, &size, TID_UINT32, true);
6644 0 : if (!stop_requested && !in_stop_transition && local_state != STATE_STOPPED &&
6645 0 : duration > 0 && ss_time() >= run_start_time + duration) {
6646 0 : cm_msg(MTALK, "main", "stopping run after %d seconds", duration);
6647 0 : status = stop_the_run(1);
6648 : }
6649 :
6650 : /* stop the run if previous attempt failed */
6651 0 : if (stop_try_later) {
6652 0 : if (ss_time_sec() > stop_try_later) {
6653 0 : cm_msg(MTALK, "main", "another attempt to stop the run");
6654 0 : status = stop_the_run(0);
6655 : }
6656 : }
6657 :
6658 : /* check keyboard once every 100 ms */
6659 0 : if (ss_millitime() - last_time_kb > 100) {
6660 0 : last_time_kb = ss_millitime();
6661 :
6662 0 : ch = 0;
6663 0 : while (ss_kbhit()) {
6664 0 : ch = ss_getchar(0);
6665 0 : if (ch == -1)
6666 0 : ch = getchar();
6667 :
6668 0 : if ((char) ch == '!')
6669 0 : break;
6670 : }
6671 : }
6672 :
6673 0 : } while (msg != RPC_SHUTDOWN && msg != SS_ABORT && ch != '!');
6674 :
6675 : /* reset terminal */
6676 0 : ss_getchar(TRUE);
6677 :
6678 : /* close history logging */
6679 0 : close_history();
6680 :
6681 : /* stop image history threads */
6682 0 : if (get_number_image_history_threads() > 0) {
6683 0 : printf("Stopping image history threads...");
6684 0 : fflush(stdout);
6685 0 : stop_image_history();
6686 0 : printf("ok\n");
6687 : }
6688 :
6689 0 : cm_disconnect_experiment();
6690 :
6691 0 : return 0;
6692 : }
6693 :
6694 : /* emacs
6695 : * Local Variables:
6696 : * tab-width: 8
6697 : * c-basic-offset: 3
6698 : * indent-tabs-mode: nil
6699 : * End:
6700 : */
|