LCOV - code coverage report
Current view: top level - progs - mlogger.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 2931 0
Test Date: 2025-11-11 10:26:08 Functions: 0.0 % 143 0

            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              :  */
        

Generated by: LCOV version 2.0-1