mlogger.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 
00003   Name:         mlogger.c
00004   Created by:   Stefan Ritt
00005 
00006   Contents:     MIDAS logger program
00007 
00008   $Id: mlogger.c 4837 2010-09-27 22:39:34Z amaudruz $
00009 
00010 \********************************************************************/
00011 
00012 #include "midas.h"
00013 #include "msystem.h"
00014 #include "hardware.h"
00015 #include <errno.h>              /* for mkdir() */
00016 #include <assert.h>
00017 
00018 #define HAVE_LOGGING
00019 #include "mdsupport.h"
00020 
00021 #ifdef HAVE_ROOT
00022 #undef GetCurrentTime
00023 #include "TApplication.h"
00024 #include "TFile.h"
00025 #include "TNtuple.h"
00026 #include "TLeaf.h"
00027 #endif
00028 
00029 #ifdef HAVE_ZLIB
00030 #include <zlib.h>
00031 #endif
00032 
00033 #ifdef HAVE_MYSQL
00034 #ifdef OS_UNIX
00035 #include <mysql/mysql.h>
00036 #include <mysql/mysqld_error.h>
00037 #endif
00038 #ifdef OS_WINNT
00039 #include <mysql.h>
00040 #include <mysqld_error.h>
00041 int errno;                      // under NT, "ignore libcd" is required, so errno has to be defined here
00042 #endif
00043 void create_sql_tree();
00044 #endif
00045 
00046 /*---- globals -----------------------------------------------------*/
00047 
00048 #define LOGGER_TIMEOUT 60000
00049 
00050 #define MAX_CHANNELS 10
00051 
00052 INT  local_state;
00053 BOOL in_stop_transition = FALSE;
00054 BOOL tape_message = TRUE;
00055 BOOL verbose = FALSE;
00056 BOOL stop_requested = FALSE;
00057 BOOL start_requested = FALSE;
00058 DWORD auto_restart = 0;
00059 DWORD run_start_time, subrun_start_time;
00060 
00061 LOG_CHN log_chn[MAX_CHANNELS];
00062 
00063 struct hist_log_s {
00064    char event_name[256];
00065    WORD event_id;
00066    char *buffer;
00067    INT buffer_size;
00068    HNDLE hKeyVar;
00069    DWORD n_var;
00070    DWORD period;
00071    DWORD last_log;
00072 };
00073 
00074 static int         hist_log_size = 0;
00075 static int         hist_log_max = 0;
00076 static hist_log_s *hist_log = NULL;
00077 
00078 HNDLE hDB;
00079 
00080 /*---- ODB records -------------------------------------------------*/
00081 
00082 CHN_SETTINGS_STR(chn_settings_str);
00083 
00084 /*---- data structures for MIDAS format ----------------------------*/
00085 
00086 typedef struct {
00087    char *buffer;
00088    char *write_pointer;
00089 } MIDAS_INFO;
00090 
00091 /*---- forward declarations ----------------------------------------*/
00092 
00093 void receive_event(HNDLE hBuf, HNDLE request_id, EVENT_HEADER * pheader, void *pevent);
00094 INT log_write(LOG_CHN * log_chn, EVENT_HEADER * pheader);
00095 void log_system_history(HNDLE hDB, HNDLE hKey, void *info);
00096 int log_generate_file_name(LOG_CHN *log_chn);
00097 
00098 /*== common code FAL/MLOGGER start =================================*/
00099 
00100 /*---- Logging initialization --------------------------------------*/
00101 
00102 void logger_init()
00103 {
00104    INT size, status, delay, index;
00105    BOOL flag;
00106    HNDLE hKey, hKeyChannel;
00107    KEY key;
00108    char str[256];
00109 
00110    /*---- create /logger entries -----*/
00111 
00112    cm_get_path(str);
00113    size = sizeof(str);
00114    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
00115 
00116    strcpy(str, "midas.log");
00117    size = sizeof(str);
00118    db_get_value(hDB, 0, "/Logger/Message file", str, &size, TID_STRING, TRUE);
00119 
00120    size = sizeof(BOOL);
00121    flag = TRUE;
00122    db_get_value(hDB, 0, "/Logger/Write data", &flag, &size, TID_BOOL, TRUE);
00123 
00124    flag = FALSE;
00125    db_get_value(hDB, 0, "/Logger/ODB Dump", &flag, &size, TID_BOOL, TRUE);
00126 
00127    strcpy(str, "run%05d.odb");
00128    size = sizeof(str);
00129    db_get_value(hDB, 0, "/Logger/ODB Dump File", str, &size, TID_STRING, TRUE);
00130 
00131    flag = FALSE;
00132    size = sizeof(BOOL);
00133    db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
00134 
00135    delay = 0;
00136    size = sizeof(INT);
00137    db_get_value(hDB, 0, "/Logger/Auto restart delay", &delay, &size, TID_INT, TRUE);
00138 
00139    flag = TRUE;
00140    db_get_value(hDB, 0, "/Logger/Tape message", &flag, &size, TID_BOOL, TRUE);
00141 
00142    /* create at least one logging channel */
00143    status = db_find_key(hDB, 0, "/Logger/Channels/0", &hKey);
00144    if (status != DB_SUCCESS) {
00145       /* if no channels are defined, define at least one */
00146       status = db_create_record(hDB, 0, "/Logger/Channels/0", strcomb(chn_settings_str));
00147       if (status != DB_SUCCESS)
00148          cm_msg(MERROR, "logger_init", "Cannot create channel entry in database");
00149    } else {
00150       /* check format of other channels */
00151       status = db_find_key(hDB, 0, "/Logger/Channels", &hKey);
00152       if (status == DB_SUCCESS) {
00153          for (index = 0; index < MAX_CHANNELS; index++) {
00154             status = db_enum_key(hDB, hKey, index, &hKeyChannel);
00155             if (status == DB_NO_MORE_SUBKEYS)
00156                break;
00157 
00158             db_get_key(hDB, hKeyChannel, &key);
00159             status = db_check_record(hDB, hKey, key.name, strcomb(chn_settings_str), TRUE);
00160             if (status != DB_SUCCESS && status != DB_OPEN_RECORD) {
00161                cm_msg(MERROR, "logger_init", "Cannot create/check channel record");
00162                break;
00163             }
00164          }
00165       }
00166    }
00167 #ifdef HAVE_MYSQL
00168    create_sql_tree();
00169 #endif
00170 }
00171 
00172 /*---- ODB dump routine --------------------------------------------*/
00173 
00174 void log_odb_dump(LOG_CHN * log_chn, short int event_id, INT run_number)
00175 {
00176    INT status, buffer_size, size;
00177    EVENT_HEADER *pevent;
00178 
00179    /* write ODB dump */
00180    buffer_size = 100000;
00181    do {
00182       pevent = (EVENT_HEADER *) malloc(buffer_size);
00183       if (pevent == NULL) {
00184          cm_msg(MERROR, "log_odb_dump", "Cannot allocate ODB dump buffer");
00185          break;
00186       }
00187 
00188       size = buffer_size - sizeof(EVENT_HEADER);
00189       status = db_copy_xml(hDB, 0, (char *) (pevent + 1), &size);
00190 
00191       /* following line would dump ODB in old ASCII format instead of XML */
00192       //status = db_copy(hDB, 0, (char *) (pevent + 1), &size, "");
00193       if (status != DB_TRUNCATED) {
00194          bm_compose_event(pevent, event_id, MIDAS_MAGIC,
00195                           buffer_size - sizeof(EVENT_HEADER) - size + 1, run_number);
00196          log_write(log_chn, pevent);
00197          free(pevent);
00198          break;
00199       }
00200 
00201       /* increase buffer size if truncated */
00202       free(pevent);
00203       buffer_size *= 10;
00204    } while (1);
00205 }
00206 
00207 /*---- ODB save routine --------------------------------------------*/
00208 
00209 void odb_save(const char *filename)
00210 {
00211    int size;
00212    char dir[256];
00213    char path[256];
00214 
00215    if (strchr(filename, DIR_SEPARATOR) == NULL) {
00216       size = sizeof(dir);
00217       dir[0] = 0;
00218       db_get_value(hDB, 0, "/Logger/Data Dir", dir, &size, TID_STRING, TRUE);
00219       if (dir[0] != 0)
00220          if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00221             strcat(dir, DIR_SEPARATOR_STR);
00222       strcpy(path, dir);
00223       strcat(path, filename);
00224    } else
00225       strcpy(path, filename);
00226 
00227    if (strstr(filename, ".xml") || strstr(filename, ".XML"))
00228       db_save_xml(hDB, 0, path);
00229    else
00230       db_save(hDB, 0, path, FALSE);
00231 }
00232 
00233 
00234 #ifdef HAVE_MYSQL
00235 
00236 /*==== SQL routines =================================================*/
00237 
00238 /*---- Convert ctime() type date/time to SQL 'datetime' -------------*/
00239 
00240 typedef struct {
00241    char column_name[NAME_LENGTH];
00242    char column_type[NAME_LENGTH];
00243    char data[256];
00244 } SQL_LIST;
00245 
00246 char *mname[] = {
00247    "January",
00248    "February",
00249    "March",
00250    "April",
00251    "May",
00252    "June",
00253    "July",
00254    "August",
00255    "September",
00256    "October",
00257    "November",
00258    "December"
00259 };
00260 
00261 void ctime_to_datetime(char *date)
00262 {
00263    char ctime_date[30];
00264    struct tm tms;
00265    int i;
00266 
00267    strlcpy(ctime_date, date, sizeof(ctime_date));
00268    memset(&tms, 0, sizeof(struct tm));
00269 
00270    for (i = 0; i < 12; i++)
00271       if (strncmp(ctime_date + 4, mname[i], 3) == 0)
00272          break;
00273    tms.tm_mon = i;
00274 
00275    tms.tm_mday = atoi(ctime_date + 8);
00276    tms.tm_hour = atoi(ctime_date + 11);
00277    tms.tm_min = atoi(ctime_date + 14);
00278    tms.tm_sec = atoi(ctime_date + 17);
00279    tms.tm_year = atoi(ctime_date + 20) - 1900;
00280    tms.tm_isdst = -1;
00281 
00282    if (tms.tm_year < 90)
00283       tms.tm_year += 100;
00284 
00285    mktime(&tms);
00286    sprintf(date, "%d-%02d-%02d %02d-%02d-%02d",
00287            tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec);
00288 }
00289 
00290 /*---- mySQL debugging output --------------------------------------*/
00291 
00292 int mysql_query_debug(MYSQL * db, char *query)
00293 {
00294    int status, size, fh;
00295    char filename[256], path[256], dir[256];
00296    HNDLE hKey;
00297 
00298    /* comment in this line if you need debugging output */
00299    //cm_msg(MINFO, "mysql_query_debug", "SQL query: %s", query);
00300 
00301    /* write query into logfile if requested */
00302    size = sizeof(filename);
00303    filename[0] = 0;
00304    db_get_value(hDB, 0, "/Logger/SQL/Logfile", filename, &size, TID_STRING, TRUE);
00305    if (filename[0]) {
00306       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00307       if (status == DB_SUCCESS) {
00308          size = sizeof(dir);
00309          memset(dir, 0, size);
00310          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00311          if (dir[0] != 0)
00312             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00313                strcat(dir, DIR_SEPARATOR_STR);
00314 
00315          strcpy(path, dir);
00316          strcat(path, filename);
00317       } else {
00318          cm_get_path(dir);
00319          if (dir[0] != 0)
00320             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00321                strcat(dir, DIR_SEPARATOR_STR);
00322 
00323          strcpy(path, dir);
00324          strcat(path, filename);
00325       }
00326 
00327       fh = open(path, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
00328       if (fh < 0) {
00329          printf("Cannot open message log file %s\n", path);
00330       } else {
00331          write(fh, query, strlen(query));
00332          write(fh, ";\n", 2);
00333          close(fh);
00334       }
00335    }
00336 
00337    /* execut sql query */
00338    status = mysql_query(db, query);
00339 
00340    if (status)
00341       cm_msg(MERROR, "mysql_query_debug", "SQL error: %s", mysql_error(db));
00342 
00343    return status;
00344 }
00345 
00346 /*---- Retrieve list of columns from ODB tree ----------------------*/
00347 
00348 int sql_get_columns(HNDLE hKeyRoot, SQL_LIST ** sql_list)
00349 {
00350    HNDLE hKey;
00351    int n, i, size, status;
00352    KEY key;
00353    char str[256], data[256];
00354 
00355    for (i = 0;; i++) {
00356       status = db_enum_key(hDB, hKeyRoot, i, &hKey);
00357       if (status == DB_NO_MORE_SUBKEYS)
00358          break;
00359    }
00360 
00361    if (i == 0)
00362       return 0;
00363 
00364    n = i;
00365 
00366    *sql_list = (SQL_LIST *) malloc(sizeof(SQL_LIST) * n);
00367 
00368    for (i = 0; i < n; i++) {
00369 
00370       /* get name of link, NOT of link target */
00371       db_enum_link(hDB, hKeyRoot, i, &hKey);
00372       db_get_link(hDB, hKey, &key);
00373       strlcpy((*sql_list)[i].column_name, key.name, NAME_LENGTH);
00374 
00375       /* get key */
00376       db_enum_key(hDB, hKeyRoot, i, &hKey);
00377       db_get_key(hDB, hKey, &key);
00378 
00379       /* get key data */
00380       size = sizeof(data);
00381       db_get_data(hDB, hKey, data, &size, key.type);
00382       db_sprintf(str, data, size, 0, key.type);
00383       if (key.type == TID_BOOL)
00384          strcpy((*sql_list)[i].data, str[0] == 'y' || str[0] == 'Y' ? "1" : "0");
00385       else
00386          strcpy((*sql_list)[i].data, str);
00387 
00388       if (key.type == TID_STRING) {
00389          /* check if string is date/time */
00390          if (strlen(str) == 24 && str[10] == ' ' && str[13] == ':') {
00391             strcpy(str, "DATETIME");
00392             ctime_to_datetime((*sql_list)[i].data);
00393          } else if (key.item_size < 256)
00394             sprintf(str, "VARCHAR (%d)", key.item_size);
00395          else
00396             sprintf(str, " TEXT");
00397 
00398       } else {
00399          switch (key.type) {
00400          case TID_BYTE:
00401             strcpy(str, "TINYINT UNSIGNED ");
00402             break;
00403          case TID_SBYTE:
00404             strcpy(str, "TINYINT  ");
00405             break;
00406          case TID_CHAR:
00407             strcpy(str, "CHAR ");
00408             break;
00409          case TID_WORD:
00410             strcpy(str, "SMALLINT UNSIGNED ");
00411             break;
00412          case TID_SHORT:
00413             strcpy(str, "SMALLINT ");
00414             break;
00415          case TID_DWORD:
00416             strcpy(str, "INT UNSIGNED ");
00417             break;
00418          case TID_INT:
00419             strcpy(str, "INT ");
00420             break;
00421          case TID_BOOL:
00422             strcpy(str, "BOOLEAN ");
00423             break;
00424          case TID_FLOAT:
00425             strcpy(str, "FLOAT ");
00426             break;
00427          case TID_DOUBLE:
00428             strcpy(str, "DOUBLE ");
00429             break;
00430          default:
00431             cm_msg(MERROR, "sql_create_database",
00432                    "No SQL type mapping for key \"%s\" of type %s", key.name, rpc_tid_name(key.type));
00433          }
00434       }
00435 
00436       strcpy((*sql_list)[i].column_type, str);
00437    }
00438 
00439    return n;
00440 }
00441 
00442 /*---- Create mySQL table from ODB tree ----------------------------*/
00443 
00444 BOOL sql_create_table(MYSQL * db, char *database, char *table, HNDLE hKeyRoot)
00445 {
00446    char str[256], query[5000];
00447    int i, n_col;
00448    SQL_LIST *sql_list;
00449 
00450    sprintf(query, "CREATE TABLE `%s`.`%s` (", database, table);
00451 
00452    n_col = sql_get_columns(hKeyRoot, &sql_list);
00453    if (n_col == 0) {
00454       db_get_path(hDB, hKeyRoot, str, sizeof(str));
00455       cm_msg(MERROR, "sql_create_database", "ODB tree \"%s\" contains no variables", str);
00456       return FALSE;
00457    }
00458 
00459    for (i = 0; i < n_col; i++)
00460       sprintf(query + strlen(query), "`%s` %s  NOT NULL, ", sql_list[i].column_name, sql_list[i].column_type);
00461 
00462    sprintf(query + strlen(query), "PRIMARY KEY (`%s`))", sql_list[0].column_name);
00463    free(sql_list);
00464 
00465    if (mysql_query_debug(db, query)) {
00466       cm_msg(MERROR, "sql_create_table", "Failed to create table: Error: %s", mysql_error(db));
00467       return FALSE;
00468    }
00469 
00470    return TRUE;
00471 }
00472 
00473 /*---- Create mySQL table from ODB tree ----------------------------*/
00474 
00475 BOOL sql_modify_table(MYSQL * db, char *database, char *table, HNDLE hKeyRoot)
00476 {
00477    char str[256], query[5000];
00478    int i, n_col;
00479    SQL_LIST *sql_list;
00480 
00481    n_col = sql_get_columns(hKeyRoot, &sql_list);
00482    if (n_col == 0) {
00483       db_get_path(hDB, hKeyRoot, str, sizeof(str));
00484       cm_msg(MERROR, "sql_modify_table", "ODB tree \"%s\" contains no variables", str);
00485       return FALSE;
00486    }
00487 
00488    for (i = 0; i < n_col; i++) {
00489 
00490       /* try to add column */
00491       if (i == 0)
00492          sprintf(query, "ALTER TABLE `%s`.`%s` ADD `%s` %s",
00493                  database, table, sql_list[i].column_name, sql_list[i].column_type);
00494       else
00495          sprintf(query, "ALTER TABLE `%s`.`%s` ADD `%s` %s AFTER `%s`",
00496                  database, table, sql_list[i].column_name, sql_list[i].column_type,
00497                  sql_list[i - 1].column_name);
00498 
00499       if (mysql_query_debug(db, query)) {
00500          if (mysql_errno(db) == ER_DUP_FIELDNAME) {
00501 
00502             /* try to modify column */
00503             sprintf(query, "ALTER TABLE `%s`.`%s` MODIFY `%s` %s",
00504                     database, table, sql_list[i].column_name, sql_list[i].column_type);
00505 
00506             if (mysql_query_debug(db, query)) {
00507                free(sql_list);
00508                cm_msg(MERROR, "sql_modify_table", "Failed to modify column: Error: %s", mysql_error(db));
00509                return FALSE;
00510             }
00511 
00512          } else {
00513             free(sql_list);
00514             cm_msg(MERROR, "sql_modify_table", "Failed to add column: Error: %s", mysql_error(db));
00515             return FALSE;
00516          }
00517       }
00518    }
00519 
00520    cm_msg(MINFO, "sql_insert", "SQL table '%s.%s' modified successfully", database, table);
00521 
00522    return TRUE;
00523 }
00524 
00525 /*---- Create mySQL database ---------------------------------------*/
00526 
00527 BOOL sql_create_database(MYSQL * db, char *database)
00528 {
00529    char query[256];
00530 
00531    sprintf(query, "CREATE DATABASE `%s`", database);
00532    if (mysql_query_debug(db, query)) {
00533       cm_msg(MERROR, "sql_create_database", "Failed to create database: Error: %s", mysql_error(db));
00534       return FALSE;
00535    }
00536 
00537    /* select database */
00538    sprintf(query, "USE `%s`", database);
00539    if (mysql_query_debug(db, query)) {
00540       cm_msg(MERROR, "sql_create_database", "Failed to select database: Error: %s", mysql_error(db));
00541       return FALSE;
00542    }
00543 
00544    return TRUE;
00545 }
00546 
00547 /*---- Insert table row from ODB tree -------------------------------*/
00548 
00549 int sql_insert(MYSQL * db, char *database, char *table, HNDLE hKeyRoot, BOOL create_flag)
00550 {
00551    char query[5000], str[5000];
00552    int status, i, n_col;
00553    SQL_LIST *sql_list;
00554 
00555    /* 
00556       build SQL query in the form 
00557       "INSERT INTO `<table>` (`<name>`, <name`,..) VALUES (`<value>`, `value`, ...) 
00558     */
00559    sprintf(query, "INSERT INTO `%s`.`%s` (", database, table);
00560    n_col = sql_get_columns(hKeyRoot, &sql_list);
00561    if (n_col == 0)
00562       return DB_SUCCESS;
00563 
00564    for (i = 0; i < n_col; i++) {
00565       sprintf(query + strlen(query), "`%s`", sql_list[i].column_name);
00566       if (i < n_col - 1)
00567          strcat(query, ", ");
00568    }
00569 
00570    strlcat(query, ") VALUES (", sizeof(query));
00571 
00572    for (i = 0; i < n_col; i++) {
00573       strlcat(query, "'", sizeof(query));
00574 
00575       mysql_escape_string(str, sql_list[i].data, strlen(sql_list[i].data));
00576       strlcat(query, str, sizeof(query));
00577       strlcat(query, "'", sizeof(query));
00578 
00579       if (i < n_col - 1)
00580          strlcat(query, ", ", sizeof(query));
00581    }
00582 
00583    free(sql_list);
00584    strlcat(query, ")", sizeof(query));
00585    if (mysql_query_debug(db, query)) {
00586 
00587       /* if entry for this run exists alreay return */
00588       if (mysql_errno(db) == ER_DUP_ENTRY) {
00589 
00590          return ER_DUP_ENTRY;
00591 
00592       } else if (mysql_errno(db) == ER_NO_SUCH_TABLE && create_flag) {
00593 
00594          /* if table does not exist, creat it and try again */
00595          sql_create_table(db, database, table, hKeyRoot);
00596          if (mysql_query_debug(db, query)) {
00597             cm_msg(MERROR, "sql_insert", "Failed to update database: Error: %s", mysql_error(db));
00598             return mysql_errno(db);
00599          }
00600          cm_msg(MINFO, "sql_insert", "SQL table '%s.%s' created successfully", database, table);
00601 
00602       } else if (mysql_errno(db) == ER_BAD_FIELD_ERROR && create_flag) {
00603 
00604          /* if table structure is different, adjust it and try again */
00605          sql_modify_table(db, database, table, hKeyRoot);
00606          if (mysql_query_debug(db, query)) {
00607             cm_msg(MERROR, "sql_insert", "Failed to update database: Error: %s", mysql_error(db));
00608             return mysql_errno(db);
00609          }
00610 
00611       } else {
00612          status = mysql_errno(db);
00613          cm_msg(MERROR, "sql_insert", "Failed to update database: Error: %s", mysql_error(db));
00614          return mysql_errno(db);
00615       }
00616    }
00617 
00618    return DB_SUCCESS;
00619 }
00620 
00621 /*---- Update table row from ODB tree ------------------------------*/
00622 
00623 int sql_update(MYSQL * db, char *database, char *table, HNDLE hKeyRoot, BOOL create_flag, char *where)
00624 {
00625    char query[5000], str[10000];
00626    int i, n_col;
00627    SQL_LIST *sql_list;
00628 
00629    /* 
00630       build SQL query in the form 
00631       "UPDATE `<database`.`<table>` SET `<name>`='<value', ... WHERE `<name>`='value' 
00632     */
00633 
00634    sprintf(query, "UPDATE `%s`.`%s` SET ", database, table);
00635    n_col = sql_get_columns(hKeyRoot, &sql_list);
00636    if (n_col == 0)
00637       return DB_SUCCESS;
00638 
00639    for (i = 0; i < n_col; i++) {
00640       mysql_escape_string(str, sql_list[i].data, strlen(sql_list[i].data));
00641       sprintf(query + strlen(query), "`%s`='%s'", sql_list[i].column_name, str);
00642       if (i < n_col - 1)
00643          strcat(query, ", ");
00644    }
00645    free(sql_list);
00646 
00647    sprintf(query + strlen(query), " %s", where);
00648    if (mysql_query_debug(db, query)) {
00649       if (mysql_errno(db) == ER_NO_SUCH_TABLE && create_flag) {
00650 
00651          /* if table does not exist, creat it and try again */
00652          sql_create_table(db, database, table, hKeyRoot);
00653          return sql_insert(db, database, table, hKeyRoot, create_flag);
00654 
00655       } else if (mysql_errno(db) == ER_BAD_FIELD_ERROR && create_flag) {
00656 
00657          /* if table structure is different, adjust it and try again */
00658          sql_modify_table(db, database, table, hKeyRoot);
00659          if (mysql_query_debug(db, query)) {
00660             cm_msg(MERROR, "sql_update", "Failed to update database: Error: %s", mysql_error(db));
00661             return mysql_errno(db);
00662          }
00663 
00664       } else {
00665          cm_msg(MERROR, "sql_update", "Failed to update database: Error: %s", mysql_error(db));
00666          return mysql_errno(db);
00667       }
00668    }
00669 
00670    return DB_SUCCESS;
00671 }
00672 
00673 /*---- Create /Logger/SQL tree -------------------------------------*/
00674 
00675 void create_sql_tree()
00676 {
00677    char hostname[80], username[80], password[80], database[80], table[80], filename[80];
00678    int size, write_flag, create_flag;
00679    HNDLE hKeyRoot, hKey;
00680 
00681    size = sizeof(create_flag);
00682    create_flag = 0;
00683    db_get_value(hDB, 0, "/Logger/SQL/Create database", &create_flag, &size, TID_BOOL, TRUE);
00684 
00685    size = sizeof(write_flag);
00686    write_flag = 0;
00687    db_get_value(hDB, 0, "/Logger/SQL/Write data", &write_flag, &size, TID_BOOL, TRUE);
00688 
00689    size = sizeof(hostname);
00690    strcpy(hostname, "localhost");
00691    db_get_value(hDB, 0, "/Logger/SQL/Hostname", hostname, &size, TID_STRING, TRUE);
00692 
00693    size = sizeof(username);
00694    strcpy(username, "root");
00695    db_get_value(hDB, 0, "/Logger/SQL/Username", username, &size, TID_STRING, TRUE);
00696 
00697    size = sizeof(password);
00698    password[0] = 0;
00699    db_get_value(hDB, 0, "/Logger/SQL/Password", password, &size, TID_STRING, TRUE);
00700 
00701    /* use experiment name as default database name */
00702    size = sizeof(database);
00703    db_get_value(hDB, 0, "/Experiment/Name", database, &size, TID_STRING, TRUE);
00704    db_get_value(hDB, 0, "/Logger/SQL/Database", database, &size, TID_STRING, TRUE);
00705 
00706    size = sizeof(table);
00707    strcpy(table, "Runlog");
00708    db_get_value(hDB, 0, "/Logger/SQL/Table", table, &size, TID_STRING, TRUE);
00709 
00710    size = sizeof(filename);
00711    strcpy(filename, "sql.log");
00712    db_get_value(hDB, 0, "/Logger/SQL/Logfile", filename, &size, TID_STRING, TRUE);
00713 
00714    db_find_key(hDB, 0, "/Logger/SQL/Links BOR", &hKeyRoot);
00715    if (!hKeyRoot) {
00716       /* create some default links */
00717       db_create_key(hDB, 0, "/Logger/SQL/Links BOR", TID_KEY);
00718 
00719       if (db_find_key(hDB, 0, "/Runinfo/Run number", &hKey) == DB_SUCCESS)
00720          db_create_link(hDB, 0, "/Logger/SQL/Links BOR/Run number", "/Runinfo/Run number");
00721 
00722       if (db_find_key(hDB, 0, "/Experiment/Run parameters/Comment", &hKey) == DB_SUCCESS)
00723          db_create_link(hDB, 0, "/Logger/SQL/Links BOR/Comment", "/Experiment/Run parameters/Comment");
00724 
00725       if (db_find_key(hDB, 0, "/Runinfo/Start time", &hKey) == DB_SUCCESS)
00726          db_create_link(hDB, 0, "/Logger/SQL/Links BOR/Start time", "/Runinfo/Start time");
00727    }
00728 
00729    db_find_key(hDB, 0, "/Logger/SQL/Links EOR", &hKeyRoot);
00730    if (!hKeyRoot) {
00731       /* create some default links */
00732       db_create_key(hDB, 0, "/Logger/SQL/Links EOR", TID_KEY);
00733 
00734       if (db_find_key(hDB, 0, "/Runinfo/Stop time", &hKey) == DB_SUCCESS)
00735          db_create_link(hDB, 0, "/Logger/SQL/Links EOR/Stop time", "/Runinfo/Stop time");
00736 
00737       if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/Events sent", &hKey) == DB_SUCCESS)
00738          db_create_link(hDB, 0, "/Logger/SQL/Links EOR/Number of events",
00739                         "/Equipment/Trigger/Statistics/Events sent");
00740 
00741    }
00742 }
00743 
00744 /*---- Write ODB tree to SQL table ----------------------------------*/
00745 
00746 void write_sql(BOOL bor)
00747 {
00748    MYSQL db;
00749    char hostname[80], username[80], password[80], database[80], table[80], query[5000], where[500];
00750    int status, size, write_flag, create_flag;
00751    BOOL insert;
00752    HNDLE hKey, hKeyRoot;
00753    SQL_LIST *sql_list;
00754 
00755    /* do not update SQL if logger does not write data */
00756    size = sizeof(BOOL);
00757    write_flag = FALSE;
00758    db_get_value(hDB, 0, "/Logger/Write data", &write_flag, &size, TID_BOOL, TRUE);
00759    if (!write_flag)
00760       return;
00761 
00762    /* insert SQL on bor, else update */
00763    insert = bor;
00764 
00765    /* determine primary key */
00766    db_find_key(hDB, 0, "/Logger/SQL/Links BOR", &hKeyRoot);
00767    status = db_enum_link(hDB, hKeyRoot, 0, &hKey);
00768 
00769    /* if BOR list empty, take first one from EOR list */
00770    if (status == DB_NO_MORE_SUBKEYS) {
00771       insert = TRUE;
00772       db_find_key(hDB, 0, "/Logger/SQL/Links EOR", &hKeyRoot);
00773       status = db_enum_link(hDB, hKeyRoot, 0, &hKey);
00774       if (status == DB_NO_MORE_SUBKEYS)
00775          return;
00776    }
00777 
00778    sql_get_columns(hKeyRoot, &sql_list);
00779    sprintf(where, "WHERE `%s`='%s'", sql_list[0].column_name, sql_list[0].data);
00780    free(sql_list);
00781 
00782    /* get BOR or EOR list */
00783    if (bor) {
00784       db_find_key(hDB, 0, "/Logger/SQL/Links BOR", &hKeyRoot);
00785       if (!hKeyRoot) {
00786          cm_msg(MERROR, "write_sql", "Cannot find \"/Logger/SQL/Links BOR");
00787          return;
00788       }
00789    } else {
00790       db_find_key(hDB, 0, "/Logger/SQL/Links EOR", &hKeyRoot);
00791       if (!hKeyRoot) {
00792          cm_msg(MERROR, "write_sql", "Cannot find \"/Logger/SQL/Links EOR");
00793          return;
00794       }
00795    }
00796 
00797    size = sizeof(create_flag);
00798    create_flag = 0;
00799    db_get_value(hDB, 0, "/Logger/SQL/Create database", &create_flag, &size, TID_BOOL, TRUE);
00800 
00801    size = sizeof(write_flag);
00802    write_flag = 0;
00803    db_get_value(hDB, 0, "/Logger/SQL/Write data", &write_flag, &size, TID_BOOL, TRUE);
00804 
00805    size = sizeof(hostname);
00806    strcpy(hostname, "localhost");
00807    db_get_value(hDB, 0, "/Logger/SQL/Hostname", hostname, &size, TID_STRING, TRUE);
00808 
00809    size = sizeof(username);
00810    strcpy(username, "root");
00811    db_get_value(hDB, 0, "/Logger/SQL/Username", username, &size, TID_STRING, TRUE);
00812 
00813    size = sizeof(password);
00814    password[0] = 0;
00815    db_get_value(hDB, 0, "/Logger/SQL/Password", password, &size, TID_STRING, TRUE);
00816 
00817    /* use experiment name as default database name */
00818    size = sizeof(database);
00819    db_get_value(hDB, 0, "/Experiment/Name", database, &size, TID_STRING, TRUE);
00820    db_get_value(hDB, 0, "/Logger/SQL/Database", database, &size, TID_STRING, TRUE);
00821 
00822    size = sizeof(table);
00823    strcpy(table, "Runlog");
00824    db_get_value(hDB, 0, "/Logger/SQL/Table", table, &size, TID_STRING, TRUE);
00825 
00826    /* continue only if data should be written */
00827    if (!write_flag)
00828       return;
00829 
00830    /* connect to MySQL database */
00831    mysql_init(&db);
00832 
00833    if (!mysql_real_connect(&db, hostname, username, password, NULL, 0, NULL, 0)) {
00834       cm_msg(MERROR, "write_sql", "Failed to connect to database: Error: %s", mysql_error(&db));
00835       mysql_close(&db);
00836       return;
00837    }
00838 
00839    /* select database */
00840    sprintf(query, "USE `%s`", database);
00841    if (mysql_query_debug(&db, query)) {
00842 
00843       /* create database if selected */
00844       if (create_flag) {
00845          if (!sql_create_database(&db, database)) {
00846             mysql_close(&db);
00847             return;
00848          }
00849          cm_msg(MINFO, "write_sql", "Database \"%s\" created successfully", database);
00850 
00851       } else {
00852          cm_msg(MERROR, "write_sql", "Failed to select database: Error: %s", mysql_error(&db));
00853          mysql_close(&db);
00854          return;
00855       }
00856    }
00857 
00858    if (insert) {
00859       status = sql_insert(&db, database, table, hKeyRoot, create_flag);
00860       if (status == ER_DUP_ENTRY)
00861          sql_update(&db, database, table, hKeyRoot, create_flag, where);
00862    } else
00863       sql_update(&db, database, table, hKeyRoot, create_flag, where);
00864 
00865    mysql_close(&db);
00866 }
00867 
00868 #endif                          // HAVE_MYSQL
00869 
00870 /*---- open tape and check for data --------------------------------*/
00871 
00872 INT tape_open(char *dev, INT * handle)
00873 {
00874    INT status, count;
00875    char buffer[16];
00876 
00877    status = ss_tape_open(dev, O_RDWR | O_CREAT | O_TRUNC, handle);
00878    if (status != SS_SUCCESS)
00879       return status;
00880 
00881    /* check if tape contains data */
00882    count = sizeof(buffer);
00883    status = ss_tape_read(*handle, buffer, &count);
00884 
00885    if (count == sizeof(buffer)) {
00886       /* tape contains data -> don't start */
00887       ss_tape_rskip(*handle, -1);
00888       cm_msg(MINFO, "tape_open", "Tape contains data, please spool tape with 'mtape seod'");
00889       cm_msg(MINFO, "tape_open", "or erase it with 'mtape weof', 'mtape rewind', then try again.");
00890       ss_tape_close(*handle);
00891       return SS_TAPE_ERROR;
00892    }
00893 
00894    return SS_SUCCESS;
00895 }
00896 
00897 /*---- open FTP channel --------------------------------------------*/
00898 
00899 INT ftp_error(char *message)
00900 {
00901    cm_msg(MERROR, "ftp_error", message);
00902    return 1;
00903 }
00904 
00905 INT ftp_open(char *destination, FTP_CON ** con)
00906 {
00907    INT status;
00908    short port = 0;
00909    char *token, host_name[HOST_NAME_LENGTH],
00910        user[32], pass[32], directory[256], file_name[256], file_mode[256];
00911 
00912    /*
00913       destination should have the form:
00914       host, port, user, password, directory, run%05d.mid
00915     */
00916 
00917    /* break destination in components */
00918    token = strtok(destination, ",");
00919    if (token)
00920       strcpy(host_name, token);
00921 
00922    token = strtok(NULL, ", ");
00923    if (token)
00924       port = atoi(token);
00925 
00926    token = strtok(NULL, ", ");
00927    if (token)
00928       strcpy(user, token);
00929 
00930    token = strtok(NULL, ", ");
00931    if (token)
00932       strcpy(pass, token);
00933 
00934    token = strtok(NULL, ", ");
00935    if (token)
00936       strcpy(directory, token);
00937 
00938    token = strtok(NULL, ", ");
00939    if (token)
00940       strcpy(file_name, token);
00941 
00942    token = strtok(NULL, ", ");
00943    file_mode[0] = 0;
00944    if (token)
00945       strcpy(file_mode, token);
00946 
00947 #ifdef FAL_MAIN
00948    ftp_debug(NULL, ftp_error);
00949 #else
00950    ftp_debug((int (*)(char *)) puts, ftp_error);
00951 #endif
00952 
00953    status = ftp_login(con, host_name, port, user, pass, "");
00954    if (status >= 0)
00955       return status;
00956 
00957    status = ftp_chdir(*con, directory);
00958    if (status >= 0)
00959       return status;
00960 
00961    status = ftp_binary(*con);
00962    if (status >= 0)
00963       return status;
00964 
00965    if (file_mode[0]) {
00966       status = ftp_command(*con, "umask %s", file_mode, 200, 250, EOF);
00967       if (status >= 0)
00968          return status;
00969    }
00970 
00971    if (ftp_open_write(*con, file_name) >= 0)
00972       return (*con)->err_no;
00973 
00974    return SS_SUCCESS;
00975 }
00976 
00977 /*---- MIDAS format routines ---------------------------------------*/
00978 
00979 INT midas_flush_buffer(LOG_CHN * log_chn)
00980 {
00981    INT size, written;
00982    MIDAS_INFO *info;
00983 
00984    info = (MIDAS_INFO *) log_chn->format_info;
00985    size = (POINTER_T) info->write_pointer - (POINTER_T) info->buffer;
00986 
00987    if (size == 0)
00988       return 0;
00989 
00990    /* write record to device */
00991    if (log_chn->type == LOG_TYPE_TAPE)
00992       written = ss_tape_write(log_chn->handle, info->buffer, size);
00993    else if (log_chn->type == LOG_TYPE_FTP)
00994       written =
00995           ftp_send(((FTP_CON *) log_chn->ftp_con)->data, info->buffer,
00996                    size) == size ? SS_SUCCESS : SS_FILE_ERROR;
00997    else if (log_chn->gzfile) {
00998 #ifdef HAVE_ZLIB
00999       z_streamp s;
01000       INT i;
01001 
01002       s = (z_streamp) log_chn->gzfile;
01003       written = s->total_out;
01004       i = gzwrite(log_chn->gzfile, info->buffer, size);
01005       if (i != size)
01006          return -1;
01007       written = s->total_out - written;
01008 #else
01009       assert(!"this cannot happen! support for ZLIB not compiled in");
01010 #endif
01011    } else if (log_chn->handle) {
01012 #ifdef OS_WINNT
01013       WriteFile((HANDLE) log_chn->handle, info->buffer, size, (unsigned long *) &written, NULL);
01014 #else
01015       written = write(log_chn->handle, info->buffer, size);
01016 #endif
01017    } else {
01018       /* we are writing into the void!?! */
01019       written = 0;
01020    }
01021 
01022    info->write_pointer = info->buffer;
01023 
01024    return written;
01025 }
01026 
01027 
01028 /*------------------------------------------------------------------*/
01029 
01030 INT midas_write(LOG_CHN * log_chn, EVENT_HEADER * pevent, INT evt_size)
01031 {
01032    INT i, written, size_left;
01033    MIDAS_INFO *info;
01034 
01035    info = (MIDAS_INFO *) log_chn->format_info;
01036    written = 0;
01037 
01038    /* check if event fits into buffer */
01039    size_left = TAPE_BUFFER_SIZE - ((POINTER_T) info->write_pointer - (POINTER_T) info->buffer);
01040 
01041    if (size_left < evt_size) {
01042       /* copy first part of event */
01043       memcpy(info->write_pointer, pevent, size_left);
01044       info->write_pointer += size_left;
01045 
01046       /* flush buffer */
01047       written += midas_flush_buffer(log_chn);
01048       if (written < 0)
01049          return SS_FILE_ERROR;
01050 
01051       /* several writes for large events */
01052       while (evt_size - size_left >= TAPE_BUFFER_SIZE) {
01053          memcpy(info->buffer, (char *) pevent + size_left, TAPE_BUFFER_SIZE);
01054          info->write_pointer += TAPE_BUFFER_SIZE;
01055          size_left += TAPE_BUFFER_SIZE;
01056 
01057          i = midas_flush_buffer(log_chn);
01058          if (i < 0)
01059             return SS_FILE_ERROR;
01060 
01061          written += i;
01062       }
01063 
01064       /* copy remaining part of event */
01065       memcpy(info->buffer, (char *) pevent + size_left, evt_size - size_left);
01066       info->write_pointer = info->buffer + (evt_size - size_left);
01067    } else {
01068       /* copy event to buffer */
01069       memcpy(info->write_pointer, pevent, evt_size);
01070       info->write_pointer += evt_size;
01071    }
01072 
01073    /* update statistics */
01074    log_chn->statistics.events_written++;
01075    log_chn->statistics.bytes_written_uncompressed += evt_size;
01076    log_chn->statistics.bytes_written += written;
01077    log_chn->statistics.bytes_written_subrun += written;
01078    log_chn->statistics.bytes_written_total += written;
01079 
01080    return SS_SUCCESS;
01081 }
01082 
01083 /*------------------------------------------------------------------*/
01084 
01085 INT midas_log_open(LOG_CHN * log_chn, INT run_number)
01086 {
01087    MIDAS_INFO *info;
01088    INT status;
01089 
01090    /* allocate MIDAS buffer info */
01091    log_chn->format_info = (void **) malloc(sizeof(MIDAS_INFO));
01092 
01093    info = (MIDAS_INFO *) log_chn->format_info;
01094    if (info == NULL) {
01095       log_chn->handle = 0;
01096       return SS_NO_MEMORY;
01097    }
01098 
01099    /* allocate full ring buffer for that channel */
01100    if ((info->buffer = (char *) malloc(TAPE_BUFFER_SIZE)) == NULL) {
01101       free(info);
01102       log_chn->handle = 0;
01103       return SS_NO_MEMORY;
01104    }
01105 
01106    info->write_pointer = info->buffer;
01107 
01108    /* Create device channel */
01109    if (log_chn->type == LOG_TYPE_TAPE) {
01110       status = tape_open(log_chn->path, &log_chn->handle);
01111       if (status != SS_SUCCESS) {
01112          free(info->buffer);
01113          free(info);
01114          log_chn->handle = 0;
01115          return status;
01116       }
01117    } else if (log_chn->type == LOG_TYPE_FTP) {
01118       status = ftp_open(log_chn->path, &log_chn->ftp_con);
01119       if (status != SS_SUCCESS) {
01120          free(info->buffer);
01121          free(info);
01122          log_chn->handle = 0;
01123          return status;
01124       } else
01125          log_chn->handle = 1;
01126    } else {
01127       /* check if file exists */
01128       if (strstr(log_chn->path, "null") == NULL) {
01129          log_chn->handle = open(log_chn->path, O_RDONLY);
01130          if (log_chn->handle > 0) {
01131             /* check if file length is nonzero */
01132             if (lseek(log_chn->handle, 0, SEEK_END) > 0) {
01133                close(log_chn->handle);
01134                free(info->buffer);
01135                free(info);
01136                log_chn->handle = 0;
01137                return SS_FILE_EXISTS;
01138             }
01139          }
01140       }
01141 
01142       log_chn->gzfile = NULL;
01143       log_chn->handle = 0;
01144          
01145       /* check that compression level and file name match each other */
01146       if (1) {
01147          char *sufp = strstr(log_chn->path, ".gz");
01148          int isgz = sufp && sufp[3]==0;
01149          
01150          if (log_chn->compression>0 && !isgz) {
01151             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);
01152             free(info->buffer);
01153             free(info);
01154             return SS_FILE_ERROR;
01155          }
01156 
01157          if (log_chn->compression==0 && isgz) {
01158             cm_msg(MERROR, "midas_log_open",
01159                    "Output file name ends with '.gz', but compression level is zero");
01160             free(info->buffer);
01161             free(info);
01162             return SS_FILE_ERROR;
01163          }
01164       }
01165 
01166 #ifdef OS_WINNT
01167       log_chn->handle = (int) CreateFile(log_chn->path, GENERIC_WRITE, FILE_SHARE_READ, NULL,
01168                                          CREATE_ALWAYS,
01169                                          FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_SEQUENTIAL_SCAN, 0);
01170 #else
01171       log_chn->handle = open(log_chn->path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
01172 #endif
01173       if (log_chn->handle < 0) {
01174          free(info->buffer);
01175          free(info);
01176          log_chn->handle = 0;
01177          return SS_FILE_ERROR;
01178       }
01179 
01180       if (log_chn->compression > 0) {
01181 #ifdef HAVE_ZLIB
01182          log_chn->gzfile = gzdopen(log_chn->handle, "wb");
01183          if (log_chn->gzfile == NULL) {
01184             cm_msg(MERROR, "midas_log_open",
01185                    "Error: gzdopen() failed, cannot open compression stream");
01186             free(info->buffer);
01187             free(info);
01188             log_chn->handle = 0;
01189             return SS_FILE_ERROR;
01190          }
01191 
01192          gzsetparams(log_chn->gzfile, log_chn->compression, Z_DEFAULT_STRATEGY);
01193 #else
01194          cm_msg(MERROR, "midas_log_open", "Compression enabled but ZLIB support not compiled in");
01195          close(log_chn->handle);
01196          free(info->buffer);
01197          free(info);
01198          log_chn->handle = 0;
01199          return SS_FILE_ERROR;
01200 #endif
01201       }
01202    }
01203 
01204    /* write ODB dump */
01205    if (log_chn->settings.odb_dump)
01206       log_odb_dump(log_chn, EVENTID_BOR, run_number);
01207 
01208    return SS_SUCCESS;
01209 }
01210 
01211 /*------------------------------------------------------------------*/
01212 
01213 INT midas_log_close(LOG_CHN * log_chn, INT run_number)
01214 {
01215    int written;
01216 
01217    /* write ODB dump */
01218    if (log_chn->settings.odb_dump)
01219       log_odb_dump(log_chn, EVENTID_EOR, run_number);
01220 
01221    written = midas_flush_buffer(log_chn);
01222 
01223    /* update statistics */
01224    log_chn->statistics.bytes_written += written;
01225    log_chn->statistics.bytes_written_total += written;
01226 
01227    /* Write EOF if Tape */
01228    if (log_chn->type == LOG_TYPE_TAPE) {
01229       /* writing EOF mark on tape Fonly */
01230       ss_tape_write_eof(log_chn->handle);
01231       ss_tape_close(log_chn->handle);
01232    } else if (log_chn->type == LOG_TYPE_FTP) {
01233       ftp_close(log_chn->ftp_con);
01234       ftp_bye(log_chn->ftp_con);
01235    } else {
01236       if (log_chn->gzfile) {
01237 #ifdef HAVE_ZLIB
01238          z_streamp s;
01239 
01240          s = (z_streamp) log_chn->gzfile;
01241          written = s->total_out;
01242          gzflush(log_chn->gzfile, Z_FULL_FLUSH);
01243          written = s->total_out - written;
01244          gzclose(log_chn->gzfile);
01245          log_chn->statistics.bytes_written += written;
01246          log_chn->statistics.bytes_written_total += written;
01247          log_chn->gzfile = NULL;
01248 #else
01249          assert(!"this cannot happen! support for ZLIB not compiled in");
01250 #endif
01251       }
01252 #ifdef OS_WINNT
01253       CloseHandle((HANDLE) log_chn->handle);
01254 #else
01255       close(log_chn->handle);
01256 #endif
01257       log_chn->handle = 0;
01258    }
01259 
01260    free(((MIDAS_INFO *) log_chn->format_info)->buffer);
01261    free(log_chn->format_info);
01262 
01263    return SS_SUCCESS;
01264 }
01265 
01266 /*-- db_get_event_definition ---------------------------------------*/
01267 
01268 typedef struct {
01269    short int event_id;
01270    WORD format;
01271    HNDLE hDefKey;
01272 } EVENT_DEF;
01273 
01274 EVENT_DEF *db_get_event_definition(short int event_id)
01275 {
01276    INT i, index, status, size;
01277    char str[80];
01278    HNDLE hKey, hKeyRoot;
01279    WORD id;
01280 
01281 #define EVENT_DEF_CACHE_SIZE 30
01282    static EVENT_DEF *event_def = NULL;
01283 
01284    /* allocate memory for cache */
01285    if (event_def == NULL)
01286       event_def = (EVENT_DEF *) calloc(EVENT_DEF_CACHE_SIZE, sizeof(EVENT_DEF));
01287 
01288    /* lookup if event definition in cache */
01289    for (i = 0; event_def[i].event_id; i++)
01290       if (event_def[i].event_id == event_id)
01291          return &event_def[i];
01292 
01293    /* search free cache entry */
01294    for (index = 0; index < EVENT_DEF_CACHE_SIZE; index++)
01295       if (event_def[index].event_id == 0)
01296          break;
01297 
01298    if (index == EVENT_DEF_CACHE_SIZE) {
01299       cm_msg(MERROR, "db_get_event_definition", "too many event definitions");
01300       return NULL;
01301    }
01302 
01303    /* check for system events */
01304    if (event_id < 0) {
01305       event_def[index].event_id = event_id;
01306       event_def[index].format = FORMAT_ASCII;
01307       event_def[index].hDefKey = 0;
01308       return &event_def[index];
01309    }
01310 
01311    status = db_find_key(hDB, 0, "/equipment", &hKeyRoot);
01312    if (status != DB_SUCCESS) {
01313       cm_msg(MERROR, "db_get_event_definition", "cannot find /equipment entry in ODB");
01314       return NULL;
01315    }
01316 
01317    for (i = 0;; i++) {
01318       /* search for client with specific name */
01319       status = db_enum_key(hDB, hKeyRoot, i, &hKey);
01320       if (status == DB_NO_MORE_SUBKEYS) {
01321          sprintf(str, "Cannot find event id %d under /equipment", event_id);
01322          cm_msg(MERROR, "db_get_event_definition", str);
01323          return NULL;
01324       }
01325 
01326       size = sizeof(id);
01327       status = db_get_value(hDB, hKey, "Common/Event ID", &id, &size, TID_WORD, TRUE);
01328       if (status != DB_SUCCESS)
01329          continue;
01330 
01331       if (id == event_id) {
01332          /* set cache entry */
01333          event_def[index].event_id = id;
01334 
01335          size = sizeof(str);
01336          str[0] = 0;
01337          db_get_value(hDB, hKey, "Common/Format", str, &size, TID_STRING, TRUE);
01338 
01339          if (equal_ustring(str, "Fixed"))
01340             event_def[index].format = FORMAT_FIXED;
01341          else if (equal_ustring(str, "ASCII"))
01342             event_def[index].format = FORMAT_ASCII;
01343          else if (equal_ustring(str, "MIDAS"))
01344             event_def[index].format = FORMAT_MIDAS;
01345          else if (equal_ustring(str, "YBOS"))
01346             event_def[index].format = FORMAT_YBOS;
01347          else if (equal_ustring(str, "DUMP"))
01348             event_def[index].format = FORMAT_DUMP;
01349          else {
01350             cm_msg(MERROR, "db_get_event_definition", "unknown data format");
01351             event_def[index].event_id = 0;
01352             return NULL;
01353          }
01354 
01355          db_find_key(hDB, hKey, "Variables", &event_def[index].hDefKey);
01356          return &event_def[index];
01357       }
01358    }
01359 }
01360 
01361 /*---- DUMP format routines ----------------------------------------*/
01362 
01363 #define STR_INC(p,base) { p+=strlen(p); \
01364                           if (p > base+sizeof(base)) \
01365                             cm_msg(MERROR, "STR_INC", "ASCII buffer too small"); }
01366 
01367 
01368 INT dump_write(LOG_CHN * log_chn, EVENT_HEADER * pevent, INT evt_size)
01369 {
01370    INT status, size, i, j;
01371    EVENT_DEF *event_def;
01372    BANK_HEADER *pbh;
01373    BANK *pbk;
01374    BANK32 *pbk32;
01375    void *pdata;
01376    char buffer[100000], *pbuf, name[5], type_name[10];
01377    HNDLE hKey;
01378    KEY key;
01379    DWORD bkname;
01380    WORD bktype;
01381 
01382    event_def = db_get_event_definition(pevent->event_id);
01383    if (event_def == NULL)
01384       return SS_SUCCESS;
01385 
01386    /* write event header */
01387    pbuf = buffer;
01388    name[4] = 0;
01389 
01390    if (pevent->event_id == EVENTID_BOR)
01391       sprintf(pbuf, "%%ID BOR NR %d\n", pevent->serial_number);
01392    else if (pevent->event_id == EVENTID_EOR)
01393       sprintf(pbuf, "%%ID EOR NR %d\n", pevent->serial_number);
01394    else
01395       sprintf(pbuf, "%%ID %d TR %d NR %d\n", pevent->event_id, pevent->trigger_mask, pevent->serial_number);
01396    STR_INC(pbuf, buffer);
01397 
01398   /*---- MIDAS format ----------------------------------------------*/
01399    if (event_def->format == FORMAT_MIDAS) {
01400       LRS1882_DATA *lrs1882;
01401       LRS1877_DATA *lrs1877;
01402       LRS1877_HEADER *lrs1877_header;
01403 
01404       pbh = (BANK_HEADER *) (pevent + 1);
01405       bk_swap(pbh, FALSE);
01406 
01407       pbk = NULL;
01408       pbk32 = NULL;
01409       do {
01410          /* scan all banks */
01411          if (bk_is32(pbh)) {
01412             size = bk_iterate32(pbh, &pbk32, &pdata);
01413             if (pbk32 == NULL)
01414                break;
01415             bkname = *((DWORD *) pbk32->name);
01416             bktype = (WORD) pbk32->type;
01417          } else {
01418             size = bk_iterate(pbh, &pbk, &pdata);
01419             if (pbk == NULL)
01420                break;
01421             bkname = *((DWORD *) pbk->name);
01422             bktype = (WORD) pbk->type;
01423          }
01424 
01425          if (rpc_tid_size(bktype & 0xFF))
01426             size /= rpc_tid_size(bktype & 0xFF);
01427 
01428          lrs1882 = (LRS1882_DATA *) pdata;
01429          lrs1877 = (LRS1877_DATA *) pdata;
01430          lrs1877_header = (LRS1877_HEADER *) pdata;
01431 
01432          /* write bank header */
01433          *((DWORD *) name) = bkname;
01434 
01435          if ((bktype & 0xFF00) == 0)
01436             strcpy(type_name, rpc_tid_name(bktype & 0xFF));
01437          else if ((bktype & 0xFF00) == TID_LRS1882)
01438             strcpy(type_name, "LRS1882");
01439          else if ((bktype & 0xFF00) == TID_LRS1877)
01440             strcpy(type_name, "LRS1877");
01441          else if ((bktype & 0xFF00) == TID_PCOS3)
01442             strcpy(type_name, "PCOS3");
01443          else
01444             strcpy(type_name, "unknown");
01445 
01446          sprintf(pbuf, "BK %s TP %s SZ %d\n", name, type_name, size);
01447          STR_INC(pbuf, buffer);
01448 
01449          /* write data */
01450          for (i = 0; i < size; i++) {
01451             if ((bktype & 0xFF00) == 0)
01452                db_sprintf(pbuf, pdata, size, i, bktype & 0xFF);
01453 
01454             else if ((bktype & 0xFF00) == TID_LRS1882)
01455                sprintf(pbuf, "GA %d CH %02d DA %d", lrs1882[i].geo_addr, lrs1882[i].channel, lrs1882[i].data);
01456 
01457             else if ((bktype & 0xFF00) == TID_LRS1877) {
01458                if (i == 0)      /* header */
01459                   sprintf(pbuf, "GA %d BF %d CN %d",
01460                           lrs1877_header[i].geo_addr, lrs1877_header[i].buffer, lrs1877_header[i].count);
01461                else             /* data */
01462                   sprintf(pbuf, "GA %d CH %02d ED %d DA %1.1lf",
01463                           lrs1877[i].geo_addr, lrs1877[i].channel, lrs1877[i].edge, lrs1877[i].data * 0.5);
01464             }
01465 
01466             else if ((bktype & 0xFF00) == TID_PCOS3)
01467                *pbuf = '\0';
01468             else
01469                db_sprintf(pbuf, pdata, size, i, bktype & 0xFF);
01470 
01471             strcat(pbuf, "\n");
01472             STR_INC(pbuf, buffer);
01473          }
01474 
01475       } while (1);
01476    }
01477 
01478   /*---- FIXED format ----------------------------------------------*/
01479    if (event_def->format == FORMAT_FIXED) {
01480       if (event_def->hDefKey == 0)
01481          cm_msg(MERROR, "dump_write", "cannot find event definition");
01482       else {
01483          pdata = (char *) (pevent + 1);
01484          for (i = 0;; i++) {
01485             status = db_enum_key(hDB, event_def->hDefKey, i, &hKey);
01486             if (status == DB_NO_MORE_SUBKEYS)
01487                break;
01488 
01489             db_get_key(hDB, hKey, &key);
01490             sprintf(pbuf, "%s\n", key.name);
01491             STR_INC(pbuf, buffer);
01492 
01493             /* adjust for alignment */
01494             pdata = (void *) VALIGN(pdata, MIN(ss_get_struct_align(), key.item_size));
01495 
01496             for (j = 0; j < key.num_values; j++) {
01497                db_sprintf(pbuf, pdata, key.item_size, j, key.type);
01498                strcat(pbuf, "\n");
01499                STR_INC(pbuf, buffer);
01500             }
01501 
01502             /* shift data pointer to next item */
01503             pdata = ((char *) pdata) + key.item_size * key.num_values;
01504          }
01505       }
01506    }
01507 
01508   /*---- ASCII format ----------------------------------------------*/
01509    if (event_def->format == FORMAT_ASCII) {
01510       /* write event header to device */
01511       size = strlen(buffer);
01512       if (log_chn->type == LOG_TYPE_TAPE)
01513          status = ss_tape_write(log_chn->handle, buffer, size);
01514       else
01515          status = write(log_chn->handle, buffer, size) == size ? SS_SUCCESS : SS_FILE_ERROR;
01516 
01517       /* write event directly to device */
01518       size = strlen((char *) (pevent + 1));
01519       if (log_chn->type == LOG_TYPE_TAPE)
01520          status = ss_tape_write(log_chn->handle, (char *) (pevent + 1), size);
01521       else if (log_chn->type == LOG_TYPE_FTP)
01522          status = ftp_send((log_chn->ftp_con)->data, buffer, size) == size ?
01523              SS_SUCCESS : SS_FILE_ERROR;
01524       else
01525          status = write(log_chn->handle, (char *) (pevent + 1), size) == size ? SS_SUCCESS : SS_FILE_ERROR;
01526    } else {
01527       /* non-ascii format: only write buffer */
01528 
01529       /* insert empty line after each event */
01530       strcat(pbuf, "\n");
01531       size = strlen(buffer);
01532 
01533       /* write record to device */
01534       if (log_chn->type == LOG_TYPE_TAPE)
01535          status = ss_tape_write(log_chn->handle, buffer, size);
01536       else if (log_chn->type == LOG_TYPE_FTP)
01537          status = ftp_send((log_chn->ftp_con)->data, buffer, size) == size ?
01538              SS_SUCCESS : SS_FILE_ERROR;
01539       else
01540          status = write(log_chn->handle, buffer, size) == size ? SS_SUCCESS : SS_FILE_ERROR;
01541    }
01542 
01543    /* update statistics */
01544    log_chn->statistics.events_written++;
01545    log_chn->statistics.bytes_written += size;
01546    log_chn->statistics.bytes_written_total += size;
01547 
01548    return status;
01549 }
01550 
01551 /*------------------------------------------------------------------*/
01552 
01553 INT dump_log_open(LOG_CHN * log_chn, INT run_number)
01554 {
01555    INT status;
01556 
01557    /* Create device channel */
01558    if (log_chn->type == LOG_TYPE_TAPE) {
01559       status = tape_open(log_chn->path, &log_chn->handle);
01560       if (status != SS_SUCCESS) {
01561          log_chn->handle = 0;
01562          return status;
01563       }
01564    } else if (log_chn->type == LOG_TYPE_FTP) {
01565       status = ftp_open(log_chn->path, &log_chn->ftp_con);
01566       if (status != SS_SUCCESS) {
01567          log_chn->handle = 0;
01568          return status;
01569       } else
01570          log_chn->handle = 1;
01571    } else {
01572       log_chn->handle = open(log_chn->path, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
01573 
01574       if (log_chn->handle < 0) {
01575          log_chn->handle = 0;
01576          return SS_FILE_ERROR;
01577       }
01578    }
01579 
01580    /* write ODB dump */
01581    if (log_chn->settings.odb_dump)
01582       log_odb_dump(log_chn, EVENTID_BOR, run_number);
01583 
01584    return SS_SUCCESS;
01585 }
01586 
01587 /*------------------------------------------------------------------*/
01588 
01589 INT dump_log_close(LOG_CHN * log_chn, INT run_number)
01590 {
01591    /* write ODB dump */
01592    if (log_chn->settings.odb_dump)
01593       log_odb_dump(log_chn, EVENTID_EOR, run_number);
01594 
01595    /* Write EOF if Tape */
01596    if (log_chn->type == LOG_TYPE_TAPE) {
01597       /* writing EOF mark on tape only */
01598       ss_tape_write_eof(log_chn->handle);
01599       ss_tape_close(log_chn->handle);
01600    } else if (log_chn->type == LOG_TYPE_FTP) {
01601       ftp_close(log_chn->ftp_con);
01602       ftp_bye(log_chn->ftp_con);
01603    } else
01604       close(log_chn->handle);
01605 
01606    return SS_SUCCESS;
01607 }
01608 
01609 /*---- ASCII format routines ---------------------------------------*/
01610 
01611 INT ascii_write(LOG_CHN * log_chn, EVENT_HEADER * pevent, INT evt_size)
01612 {
01613    INT status, size, i, j;
01614    EVENT_DEF *event_def;
01615    BANK_HEADER *pbh;
01616    BANK *pbk;
01617    BANK32 *pbk32;
01618    void *pdata;
01619    char buffer[10000], name[5], type_name[10];
01620    char *ph, header_line[10000];
01621    char *pd, data_line[10000];
01622    HNDLE hKey;
01623    KEY key;
01624    static short int last_event_id = -1;
01625    DWORD bkname;
01626    WORD bktype;
01627 
01628    if (pevent->serial_number == 0)
01629       last_event_id = -1;
01630 
01631    event_def = db_get_event_definition(pevent->event_id);
01632    if (event_def == NULL)
01633       return SS_SUCCESS;
01634 
01635    name[4] = 0;
01636    header_line[0] = 0;
01637    data_line[0] = 0;
01638    ph = header_line;
01639    pd = data_line;
01640 
01641   /*---- MIDAS format ----------------------------------------------*/
01642    if (event_def->format == FORMAT_MIDAS) {
01643       LRS1882_DATA *lrs1882;
01644       LRS1877_DATA *lrs1877;
01645       LRS1877_HEADER *lrs1877_header;
01646 
01647       pbh = (BANK_HEADER *) (pevent + 1);
01648       bk_swap(pbh, FALSE);
01649 
01650       pbk = NULL;
01651       pbk32 = NULL;
01652       do {
01653          /* scan all banks */
01654          if (bk_is32(pbh)) {
01655             size = bk_iterate32(pbh, &pbk32, &pdata);
01656             if (pbk32 == NULL)
01657                break;
01658             bkname = *((DWORD *) pbk32->name);
01659             bktype = (WORD) pbk32->type;
01660          } else {
01661             size = bk_iterate(pbh, &pbk, &pdata);
01662             if (pbk == NULL)
01663                break;
01664             bkname = *((DWORD *) pbk->name);
01665             bktype = (WORD) pbk->type;
01666          }
01667 
01668          if (rpc_tid_size(bktype & 0xFF))
01669             size /= rpc_tid_size(bktype & 0xFF);
01670 
01671          lrs1882 = (LRS1882_DATA *) pdata;
01672          lrs1877 = (LRS1877_DATA *) pdata;
01673          lrs1877_header = (LRS1877_HEADER *) pdata;
01674 
01675          /* write bank header */
01676          *((DWORD *) name) = bkname;
01677 
01678          if ((bktype & 0xFF00) == 0)
01679             strcpy(type_name, rpc_tid_name(bktype & 0xFF));
01680          else if ((bktype & 0xFF00) == TID_LRS1882)
01681             strcpy(type_name, "LRS1882");
01682          else if ((bktype & 0xFF00) == TID_LRS1877)
01683             strcpy(type_name, "LRS1877");
01684          else if ((bktype & 0xFF00) == TID_PCOS3)
01685             strcpy(type_name, "PCOS3");
01686          else
01687             strcpy(type_name, "unknown");
01688 
01689          sprintf(ph, "%s[%d]\t", name, size);
01690          STR_INC(ph, header_line);
01691 
01692          /* write data */
01693          for (i = 0; i < size; i++) {
01694             db_sprintf(pd, pdata, size, i, bktype & 0xFF);
01695             strcat(pd, "\t");
01696             STR_INC(pd, data_line);
01697          }
01698 
01699       } while (1);
01700    }
01701 
01702   /*---- FIXED format ----------------------------------------------*/
01703    if (event_def->format == FORMAT_FIXED) {
01704       if (event_def->hDefKey == 0)
01705          cm_msg(MERROR, "ascii_write", "cannot find event definition");
01706       else {
01707          pdata = (char *) (pevent + 1);
01708          for (i = 0;; i++) {
01709             status = db_enum_key(hDB, event_def->hDefKey, i, &hKey);
01710             if (status == DB_NO_MORE_SUBKEYS)
01711                break;
01712 
01713             db_get_key(hDB, hKey, &key);
01714 
01715             /* adjust for alignment */
01716             pdata = (void *) VALIGN(pdata, MIN(ss_get_struct_align(), key.item_size));
01717 
01718             for (j = 0; j < key.num_values; j++) {
01719                if (pevent->event_id != last_event_id) {
01720                   if (key.num_values == 1)
01721                      sprintf(ph, "%s\t", key.name);
01722                   else
01723                      sprintf(ph, "%s%d\t", key.name, j);
01724 
01725                   STR_INC(ph, header_line);
01726                }
01727 
01728                db_sprintf(pd, pdata, key.item_size, j, key.type);
01729                strcat(pd, "\t");
01730                STR_INC(pd, data_line);
01731             }
01732 
01733             /* shift data pointer to next item */
01734             pdata = ((char *) pdata) + key.item_size * key.num_values;
01735          }
01736       }
01737    }
01738 
01739    if (*(pd - 1) == '\t')
01740       *(pd - 1) = '\n';
01741 
01742    if (last_event_id != pevent->event_id) {
01743       if (*(ph - 1) == '\t')
01744          *(ph - 1) = '\n';
01745       last_event_id = pevent->event_id;
01746       strcpy(buffer, header_line);
01747       strcat(buffer, data_line);
01748    } else
01749       strcpy(buffer, data_line);
01750 
01751    /* write buffer to device */
01752    size = strlen(buffer);
01753 
01754    if (log_chn->type == LOG_TYPE_TAPE)
01755       status = ss_tape_write(log_chn->handle, buffer, size);
01756    else if (log_chn->type == LOG_TYPE_FTP)
01757       status = ftp_send(log_chn->ftp_con->data, buffer, size) == size ?
01758           SS_SUCCESS : SS_FILE_ERROR;
01759    else
01760       status = write(log_chn->handle, buffer, size) == size ? SS_SUCCESS : SS_FILE_ERROR;
01761 
01762    /* update statistics */
01763    log_chn->statistics.events_written++;
01764    log_chn->statistics.bytes_written += size;
01765    log_chn->statistics.bytes_written_total += size;
01766 
01767    return status;
01768 }
01769 
01770 /*------------------------------------------------------------------*/
01771 
01772 INT ascii_log_open(LOG_CHN * log_chn, INT run_number)
01773 {
01774    INT status;
01775    EVENT_HEADER event;
01776 
01777    /* Create device channel */
01778    if (log_chn->type == LOG_TYPE_TAPE) {
01779       status = tape_open(log_chn->path, &log_chn->handle);
01780       if (status != SS_SUCCESS) {
01781          log_chn->handle = 0;
01782          return status;
01783       }
01784    } else if (log_chn->type == LOG_TYPE_FTP) {
01785       status = ftp_open(log_chn->path, &log_chn->ftp_con);
01786       if (status != SS_SUCCESS) {
01787          log_chn->handle = 0;
01788          return status;
01789       } else
01790          log_chn->handle = 1;
01791    } else {
01792       log_chn->handle = open(log_chn->path, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
01793 
01794       if (log_chn->handle < 0) {
01795          log_chn->handle = 0;
01796          return SS_FILE_ERROR;
01797       }
01798    }
01799 
01800    /* write ODB dump */
01801    if (log_chn->settings.odb_dump) {
01802       event.event_id = EVENTID_BOR;
01803       event.data_size = 0;
01804       event.serial_number = run_number;
01805 
01806       ascii_write(log_chn, &event, sizeof(EVENT_HEADER));
01807    }
01808 
01809    return SS_SUCCESS;
01810 }
01811 
01812 /*------------------------------------------------------------------*/
01813 
01814 INT ascii_log_close(LOG_CHN * log_chn, INT run_number)
01815 {
01816    /* Write EOF if Tape */
01817    if (log_chn->type == LOG_TYPE_TAPE) {
01818       /* writing EOF mark on tape only */
01819       ss_tape_write_eof(log_chn->handle);
01820       ss_tape_close(log_chn->handle);
01821    } else if (log_chn->type == LOG_TYPE_FTP) {
01822       ftp_close(log_chn->ftp_con);
01823       ftp_bye(log_chn->ftp_con);
01824    } else
01825       close(log_chn->handle);
01826 
01827    return SS_SUCCESS;
01828 }
01829 
01830 /*---- ROOT format routines ----------------------------------------*/
01831 
01832 #ifdef HAVE_ROOT
01833 
01834 #define MAX_BANKS 100
01835 
01836 typedef struct {
01837    int event_id;
01838    TTree *tree;
01839    int n_branch;
01840    DWORD branch_name[MAX_BANKS];
01841    int branch_filled[MAX_BANKS];
01842    int branch_len[MAX_BANKS];
01843    TBranch *branch[MAX_BANKS];
01844 } EVENT_TREE;
01845 
01846 typedef struct {
01847    TFile *f;
01848    int n_tree;
01849    EVENT_TREE *event_tree;
01850 } TREE_STRUCT;
01851 
01852 /*------------------------------------------------------------------*/
01853 
01854 INT root_book_trees(TREE_STRUCT * tree_struct)
01855 {
01856    int index, size, status;
01857    WORD id;
01858    char str[1000];
01859    HNDLE hKeyRoot, hKeyEq;
01860    KEY eqkey;
01861    EVENT_TREE *et;
01862 
01863    status = db_find_key(hDB, 0, "/Equipment", &hKeyRoot);
01864    if (status != DB_SUCCESS) {
01865       cm_msg(MERROR, "root_book_trees", "cannot find \"/Equipment\" entry in ODB");
01866       return 0;
01867    }
01868 
01869    tree_struct->n_tree = 0;
01870 
01871    for (index = 0;; index++) {
01872       /* loop through all events under /Equipment */
01873       status = db_enum_key(hDB, hKeyRoot, index, &hKeyEq);
01874       if (status == DB_NO_MORE_SUBKEYS)
01875          return 1;
01876 
01877       db_get_key(hDB, hKeyEq, &eqkey);
01878 
01879       /* create tree */
01880       tree_struct->n_tree++;
01881       if (tree_struct->n_tree == 1)
01882          tree_struct->event_tree = (EVENT_TREE *) malloc(sizeof(EVENT_TREE));
01883       else
01884          tree_struct->event_tree =
01885              (EVENT_TREE *) realloc(tree_struct->event_tree, sizeof(EVENT_TREE) * tree_struct->n_tree);
01886 
01887       et = tree_struct->event_tree + (tree_struct->n_tree - 1);
01888 
01889       size = sizeof(id);
01890       status = db_get_value(hDB, hKeyEq, "Common/Event ID", &id, &size, TID_WORD, TRUE);
01891       if (status != DB_SUCCESS)
01892          continue;
01893 
01894       et->event_id = id;
01895       et->n_branch = 0;
01896 
01897       /* check format */
01898       size = sizeof(str);
01899       str[0] = 0;
01900       db_get_value(hDB, hKeyEq, "Common/Format", str, &size, TID_STRING, TRUE);
01901 
01902       if (!equal_ustring(str, "MIDAS")) {
01903          cm_msg(MERROR, "root_book_events",
01904                 "ROOT output only for MIDAS events, but %s in %s format", eqkey.name, str);
01905          return 0;
01906       }
01907 
01908       /* create tree */
01909       sprintf(str, "Event \"%s\", ID %d", eqkey.name, id);
01910       et->tree = new TTree(eqkey.name, str);
01911    }
01912 
01913    return 1;
01914 }
01915 
01916 /*------------------------------------------------------------------*/
01917 
01918 INT root_book_bank(EVENT_TREE * et, HNDLE hKeyDef, int event_id, char *bank_name)
01919 {
01920    int i, status;
01921    char str[1000];
01922    HNDLE hKeyVar, hKeySubVar;
01923    KEY varkey, subvarkey;
01924 
01925    /* find definition of bank */
01926    status = db_find_key(hDB, hKeyDef, bank_name, &hKeyVar);
01927    if (status != DB_SUCCESS) {
01928       cm_msg(MERROR, "root_book_bank", "received unknown bank \"%s\" in event #%d", bank_name, event_id);
01929       return 0;
01930    }
01931 
01932    if (et->n_branch + 1 == MAX_BANKS) {
01933       cm_msg(MERROR, "root_book_bank", "max number of banks (%d) exceeded in event #%d", MAX_BANKS, event_id);
01934       return 0;
01935    }
01936 
01937    db_get_key(hDB, hKeyVar, &varkey);
01938 
01939    if (varkey.type != TID_KEY) {
01940       /* book variable length array size */
01941 
01942       sprintf(str, "n%s/I:%s[n%s]/", varkey.name, varkey.name, varkey.name);
01943 
01944       switch (varkey.type) {
01945       case TID_BYTE:
01946       case TID_CHAR:
01947          strcat(str, "b");
01948          break;
01949       case TID_SBYTE:
01950          strcat(str, "B");
01951          break;
01952       case TID_WORD:
01953          strcat(str, "s");
01954          break;
01955       case TID_SHORT:
01956          strcat(str, "S");
01957          break;
01958       case TID_DWORD:
01959          strcat(str, "i");
01960          break;
01961       case TID_INT:
01962          strcat(str, "I");
01963          break;
01964       case TID_BOOL:
01965          strcat(str, "I");
01966          break;
01967       case TID_FLOAT:
01968          strcat(str, "F");
01969          break;
01970       case TID_DOUBLE:
01971          strcat(str, "D");
01972          break;
01973       case TID_STRING:
01974          strcat(str, "C");
01975          break;
01976       }
01977 
01978       et->branch[et->n_branch] = et->tree->Branch(bank_name, NULL, str);
01979       et->branch_name[et->n_branch] = *(DWORD *) bank_name;
01980       et->n_branch++;
01981    } else {
01982       /* book structured bank */
01983       str[0] = 0;
01984 
01985       for (i = 0;; i++) {
01986          /* loop through bank variables */
01987          status = db_enum_key(hDB, hKeyVar, i, &hKeySubVar);
01988          if (status == DB_NO_MORE_SUBKEYS)
01989             break;
01990 
01991          db_get_key(hDB, hKeySubVar, &subvarkey);
01992 
01993          if (i != 0)
01994             strcat(str, ":");
01995          strcat(str, subvarkey.name);
01996          strcat(str, "/");
01997          switch (subvarkey.type) {
01998          case TID_BYTE:
01999          case TID_CHAR:
02000             strcat(str, "b");
02001             break;
02002          case TID_SBYTE:
02003             strcat(str, "B");
02004             break;
02005          case TID_WORD:
02006             strcat(str, "s");
02007             break;
02008          case TID_SHORT:
02009             strcat(str, "S");
02010             break;
02011          case TID_DWORD:
02012             strcat(str, "i");
02013             break;
02014          case TID_INT:
02015             strcat(str, "I");
02016             break;
02017          case TID_BOOL:
02018             strcat(str, "I");
02019             break;
02020          case TID_FLOAT:
02021             strcat(str, "F");
02022             break;
02023          case TID_DOUBLE:
02024             strcat(str, "D");
02025             break;
02026          case TID_STRING:
02027             strcat(str, "C");
02028             break;
02029          }
02030       }
02031 
02032       et->branch[et->n_branch] = et->tree->Branch(bank_name, NULL, str);
02033       et->branch_name[et->n_branch] = *(DWORD *) bank_name;
02034       et->n_branch++;
02035    }
02036 
02037    return 1;
02038 }
02039 
02040 /*------------------------------------------------------------------*/
02041 
02042 INT root_write(LOG_CHN * log_chn, EVENT_HEADER * pevent, INT evt_size)
02043 {
02044    INT size, i;
02045    char bank_name[32];
02046    EVENT_DEF *event_def;
02047    BANK_HEADER *pbh;
02048    void *pdata;
02049    static short int last_event_id = -1;
02050    TREE_STRUCT *ts;
02051    EVENT_TREE *et;
02052    BANK *pbk;
02053    BANK32 *pbk32;
02054    DWORD bklen;
02055    DWORD bkname;
02056    WORD bktype;
02057    TBranch *branch;
02058 
02059    if (pevent->serial_number == 0)
02060       last_event_id = -1;
02061 
02062    event_def = db_get_event_definition(pevent->event_id);
02063    if (event_def == NULL) {
02064       cm_msg(MERROR, "root_write", "Definition for event #%d not found under /Equipment", pevent->event_id);
02065       return SS_INVALID_FORMAT;
02066    }
02067 
02068    ts = (TREE_STRUCT *) log_chn->format_info;
02069 
02070    size = (INT) ts->f->GetBytesWritten();
02071 
02072   /*---- MIDAS format ----------------------------------------------*/
02073 
02074    if (event_def->format == FORMAT_MIDAS) {
02075       pbh = (BANK_HEADER *) (pevent + 1);
02076       bk_swap(pbh, FALSE);
02077 
02078       /* find event in tree structure */
02079       for (i = 0; i < ts->n_tree; i++)
02080          if (ts->event_tree[i].event_id == pevent->event_id)
02081             break;
02082 
02083       if (i == ts->n_tree) {
02084          cm_msg(MERROR, "root_write", "Event #%d not booked by root_book_events()", pevent->event_id);
02085          return SS_INVALID_FORMAT;
02086       }
02087 
02088       et = ts->event_tree + i;
02089 
02090       /* first mark all banks non-filled */
02091       for (i = 0; i < et->n_branch; i++)
02092          et->branch_filled[i] = FALSE;
02093 
02094       /* go thourgh all banks and set the address */
02095       pbk = NULL;
02096       pbk32 = NULL;
02097       do {
02098          /* scan all banks */
02099          if (bk_is32(pbh)) {
02100             bklen = bk_iterate32(pbh, &pbk32, &pdata);
02101             if (pbk32 == NULL)
02102                break;
02103             bkname = *((DWORD *) pbk32->name);
02104             bktype = (WORD) pbk32->type;
02105          } else {
02106             bklen = bk_iterate(pbh, &pbk, &pdata);
02107             if (pbk == NULL)
02108                break;
02109             bkname = *((DWORD *) pbk->name);
02110             bktype = (WORD) pbk->type;
02111          }
02112 
02113          if (rpc_tid_size(bktype & 0xFF))
02114             bklen /= rpc_tid_size(bktype & 0xFF);
02115 
02116          *((DWORD *) bank_name) = bkname;
02117          bank_name[4] = 0;
02118 
02119          for (i = 0; i < et->n_branch; i++)
02120             if (et->branch_name[i] == bkname)
02121                break;
02122 
02123          if (i == et->n_branch)
02124             root_book_bank(et, event_def->hDefKey, pevent->event_id, bank_name);
02125 
02126          branch = et->branch[i];
02127          et->branch_filled[i] = TRUE;
02128          et->branch_len[i] = bklen;
02129 
02130          if (bktype != TID_STRUCT) {
02131             TIter next(branch->GetListOfLeaves());
02132             TLeaf *leaf = (TLeaf *) next();
02133 
02134             /* varibale length array */
02135             leaf->SetAddress(&et->branch_len[i]);
02136 
02137             leaf = (TLeaf *) next();
02138             leaf->SetAddress(pdata);
02139          } else {
02140             /* structured bank */
02141             branch->SetAddress(pdata);
02142          }
02143 
02144       } while (1);
02145 
02146       /* check if all branches have been filled */
02147       for (i = 0; i < et->n_branch; i++)
02148          if (!et->branch_filled[i])
02149             cm_msg(MERROR, "root_write", "Bank %s booked but not received, tree cannot be filled", bank_name);
02150 
02151       /* fill tree */
02152       et->tree->Fill();
02153    }
02154 
02155    size = (INT) ts->f->GetBytesWritten() - size;
02156 
02157    /* update statistics */
02158    log_chn->statistics.events_written++;
02159    log_chn->statistics.bytes_written += size;
02160    log_chn->statistics.bytes_written_total += size;
02161 
02162    return SS_SUCCESS;
02163 }
02164 
02165 /*------------------------------------------------------------------*/
02166 
02167 INT root_log_open(LOG_CHN * log_chn, INT run_number)
02168 {
02169    INT size, level;
02170    char str[256], name[256];
02171    EVENT_HEADER event;
02172    TREE_STRUCT *tree_struct;
02173 
02174    /* Create device channel */
02175    if (log_chn->type == LOG_TYPE_TAPE || log_chn->type == LOG_TYPE_FTP) {
02176       cm_msg(MERROR, "root_log_open", "ROOT files can only reside on disk");
02177       log_chn->handle = 0;
02178       return -1;
02179    } else {
02180       /* check if file exists */
02181       if (strstr(log_chn->path, "null") == NULL) {
02182          log_chn->handle = open(log_chn->path, O_RDONLY);
02183          if (log_chn->handle > 0) {
02184             /* check if file length is nonzero */
02185             if (lseek(log_chn->handle, 0, SEEK_END) > 0) {
02186                close(log_chn->handle);
02187                log_chn->handle = 0;
02188                return SS_FILE_EXISTS;
02189             }
02190          }
02191       }
02192 
02193       name[0] = 0;
02194       size = sizeof(name);
02195       db_get_value(hDB, 0, "/Experiment/Name", name, &size, TID_STRING, TRUE);
02196 
02197       sprintf(str, "MIDAS exp. %s, run #%d", name, run_number);
02198 
02199       TFile *f = new TFile(log_chn->path, "create", str, 1);
02200       if (!f->IsOpen()) {
02201          delete f;
02202          log_chn->handle = 0;
02203          return SS_FILE_ERROR;
02204       }
02205       log_chn->handle = 1;
02206 
02207       /* set compression level */
02208       level = 0;
02209       size = sizeof(level);
02210       db_get_value(hDB, log_chn->settings_hkey, "Compression", &level, &size, TID_INT, FALSE);
02211       f->SetCompressionLevel(level);
02212 
02213       /* create root structure with trees and branches */
02214       tree_struct = (TREE_STRUCT *) malloc(sizeof(TREE_STRUCT));
02215       tree_struct->f = f;
02216 
02217       /* book event tree */
02218       root_book_trees(tree_struct);
02219 
02220       /* store file object in format_info */
02221       log_chn->format_info = (void **) tree_struct;
02222    }
02223 
02224    /* write ODB dump */
02225    if (log_chn->settings.odb_dump) {
02226       event.event_id = EVENTID_BOR;
02227       event.data_size = 0;
02228       event.serial_number = run_number;
02229 
02230       //root_write(log_chn, &event, sizeof(EVENT_HEADER));
02231    }
02232 
02233    return SS_SUCCESS;
02234 }
02235 
02236 /*------------------------------------------------------------------*/
02237 
02238 INT root_log_close(LOG_CHN * log_chn, INT run_number)
02239 {
02240    TREE_STRUCT *ts;
02241 
02242    ts = (TREE_STRUCT *) log_chn->format_info;
02243 
02244    /* flush and close file */
02245    ts->f->Write();
02246    ts->f->Close();
02247    delete ts->f;                // deletes also all trees and branches!
02248 
02249    /* delete event tree */
02250    free(ts->event_tree);
02251    free(ts);
02252 
02253    log_chn->format_info = NULL;
02254 
02255    return SS_SUCCESS;
02256 }
02257 
02258 #endif                          /* HAVE_ROOT */
02259 
02260 /*---- log_open ----------------------------------------------------*/
02261 
02262 INT log_open(LOG_CHN * log_chn, INT run_number)
02263 {
02264    INT status;
02265 
02266    if (equal_ustring(log_chn->settings.format, "YBOS")) {
02267      assert(!"YBOS not supported anymore");
02268    } else if (equal_ustring(log_chn->settings.format, "ASCII")) {
02269       log_chn->format = FORMAT_ASCII;
02270       status = ascii_log_open(log_chn, run_number);
02271    } else if (equal_ustring(log_chn->settings.format, "DUMP")) {
02272       log_chn->format = FORMAT_DUMP;
02273       status = dump_log_open(log_chn, run_number);
02274    } else if (equal_ustring(log_chn->settings.format, "ROOT")) {
02275 #ifdef HAVE_ROOT
02276       log_chn->format = FORMAT_ROOT;
02277       status = root_log_open(log_chn, run_number);
02278 #else
02279       return SS_NO_ROOT;
02280 #endif
02281    } else if (equal_ustring(log_chn->settings.format, "MIDAS")) {
02282       log_chn->format = FORMAT_MIDAS;
02283       status = midas_log_open(log_chn, run_number);
02284    } else
02285       return SS_INVALID_FORMAT;
02286 
02287    return status;
02288 }
02289 
02290 /*---- log_close ---------------------------------------------------*/
02291 
02292 INT log_close(LOG_CHN * log_chn, INT run_number)
02293 {
02294    if (log_chn->format == FORMAT_YBOS)
02295      assert(!"YBOS not supported anymore");
02296 
02297    if (log_chn->format == FORMAT_ASCII)
02298       ascii_log_close(log_chn, run_number);
02299 
02300    if (log_chn->format == FORMAT_DUMP)
02301       dump_log_close(log_chn, run_number);
02302 
02303 #ifdef HAVE_ROOT
02304    if (log_chn->format == FORMAT_ROOT)
02305       root_log_close(log_chn, run_number);
02306 #endif
02307 
02308    if (log_chn->format == FORMAT_MIDAS)
02309       midas_log_close(log_chn, run_number);
02310 
02311    log_chn->statistics.files_written += 1;
02312    log_chn->handle = 0;
02313    log_chn->ftp_con = NULL;
02314 
02315    return SS_SUCCESS;
02316 }
02317 
02318 /*---- log_write ---------------------------------------------------*/
02319 
02320 int stop_the_run(int restart)
02321 {
02322    int status;
02323    char errstr[256];
02324    int size, flag, trans_flag;
02325 
02326    if (restart) {
02327       size = sizeof(BOOL);
02328       flag = FALSE;
02329       db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
02330 
02331       if (flag) {
02332          start_requested = TRUE;
02333          auto_restart = 0;
02334       }
02335    }
02336 
02337    stop_requested = TRUE;
02338 
02339    size = sizeof(BOOL);
02340    flag = FALSE;
02341    db_get_value(hDB, 0, "/Logger/Async transitions", &flag, &size, TID_BOOL, TRUE);
02342 
02343    if (flag)
02344       trans_flag = ASYNC;
02345    else
02346       trans_flag = DETACH;
02347 
02348    status = cm_transition(TR_STOP, 0, errstr, sizeof(errstr), trans_flag, verbose);
02349    if (status != CM_SUCCESS) {
02350       cm_msg(MERROR, "log_write", "cannot stop the run: %s", errstr);
02351       return status;
02352    }
02353 
02354    return status;
02355 }
02356 
02357 int start_the_run()
02358 {
02359    int status, size, state, run_number;
02360    int flag, trans_flag;
02361    char errstr[256];
02362 
02363    start_requested = FALSE;
02364    auto_restart = 0;
02365 
02366    /* check if autorestart is still on */
02367    size = sizeof(BOOL);
02368    flag = FALSE;
02369    db_get_value(hDB, 0, "/Logger/Auto restart", &flag, &size, TID_BOOL, TRUE);
02370 
02371    if (!flag) {
02372       cm_msg(MINFO, "main", "Run auto restart canceled");
02373       return SUCCESS;
02374    }
02375 
02376    /* check if really stopped */
02377    size = sizeof(state);
02378    status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
02379    if (status != DB_SUCCESS) {
02380       cm_msg(MERROR, "main", "cannot get Runinfo/State in database");
02381       return status;
02382    }
02383   
02384    if (state != STATE_STOPPED)
02385       return SUCCESS;
02386 
02387    size = sizeof(run_number);
02388    status = db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
02389    assert(status == SUCCESS);
02390     
02391    if (run_number <= 0) {
02392       cm_msg(MERROR, "main", "aborting on attempt to use invalid run number %d", run_number);
02393       abort();
02394    }
02395     
02396    size = sizeof(BOOL);
02397    flag = FALSE;
02398    db_get_value(hDB, 0, "/Logger/Async transitions", &flag, &size, TID_BOOL, TRUE);
02399 
02400    if (flag)
02401       trans_flag = ASYNC;
02402    else
02403       trans_flag = DETACH;
02404 
02405    cm_msg(MTALK, "main", "starting new run");
02406    status = cm_transition(TR_START, run_number + 1, errstr, sizeof(errstr), trans_flag, verbose);
02407    if (status != CM_SUCCESS)
02408       cm_msg(MERROR, "main", "cannot restart run: %s", errstr);
02409 
02410    return status;
02411 }
02412 
02413 INT log_write(LOG_CHN * log_chn, EVENT_HEADER * pevent)
02414 {
02415    INT status = 0, size, izero;
02416    DWORD actual_time, start_time, watchdog_timeout, duration;
02417    BOOL watchdog_flag;
02418    static DWORD last_checked = 0;
02419    HNDLE htape, stats_hkey;
02420    char tape_name[256];
02421    double dzero;
02422 
02423    start_time = ss_millitime();
02424 
02425    if (log_chn->format == FORMAT_YBOS)
02426      assert(!"YBOS not supported anymore");
02427 
02428    if (log_chn->format == FORMAT_ASCII)
02429       status = ascii_write(log_chn, pevent, pevent->data_size + sizeof(EVENT_HEADER));
02430 
02431    if (log_chn->format == FORMAT_DUMP)
02432       status = dump_write(log_chn, pevent, pevent->data_size + sizeof(EVENT_HEADER));
02433 
02434    if (log_chn->format == FORMAT_MIDAS)
02435       status = midas_write(log_chn, pevent, pevent->data_size + sizeof(EVENT_HEADER));
02436 
02437 #ifdef HAVE_ROOT
02438    if (log_chn->format == FORMAT_ROOT)
02439       status = root_write(log_chn, pevent, pevent->data_size + sizeof(EVENT_HEADER));
02440 #endif
02441 
02442    actual_time = ss_millitime();
02443    if ((int) actual_time - (int) start_time > 3000)
02444       cm_msg(MINFO, "log_write", "Write operation on %s took %d ms", log_chn->path, actual_time - start_time);
02445 
02446    if (status != SS_SUCCESS && !stop_requested) {
02447       if (status == SS_IO_ERROR)
02448          cm_msg(MTALK, "log_write", "Physical IO error on %s, stopping run", log_chn->path);
02449       else
02450          cm_msg(MTALK, "log_write", "Error writing to %s, stopping run", log_chn->path);
02451 
02452       stop_the_run(0);
02453 
02454       return status;
02455    }
02456 
02457    /* check if event limit is reached to stop run */
02458    if (!stop_requested && !in_stop_transition &&
02459        log_chn->settings.event_limit > 0 &&
02460        log_chn->statistics.events_written >= log_chn->settings.event_limit) {
02461       stop_requested = TRUE;
02462 
02463       cm_msg(MTALK, "log_write", "stopping run after having received %1.0lf events",
02464              log_chn->settings.event_limit);
02465 
02466       status = stop_the_run(1);
02467       return status;
02468    }
02469 
02470    /* check if duration is reached for subrun */
02471    duration = 0;
02472    size = sizeof(duration);
02473    db_get_value(hDB, 0, "/Logger/Subrun duration", &duration, &size, TID_DWORD, true);
02474    if (!stop_requested && duration > 0 && ss_time() >= subrun_start_time + duration) {
02475       int run_number;
02476 
02477       // cm_msg(MTALK, "main", "stopping subrun after %d seconds", duration);
02478 
02479       size = sizeof(run_number);
02480       status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
02481       assert(status == SUCCESS);
02482 
02483       stop_requested = TRUE; // avoid recursive call thourgh log_odb_dump
02484       log_close(log_chn, run_number);
02485       log_chn->subrun_number++;
02486       log_chn->statistics.bytes_written_subrun = 0;
02487       log_generate_file_name(log_chn);
02488       log_open(log_chn, run_number);
02489       subrun_start_time = ss_time();
02490       stop_requested = FALSE;
02491    }
02492 
02493    /* check if byte limit is reached for subrun */
02494    if (!stop_requested && log_chn->settings.subrun_byte_limit > 0 &&
02495        log_chn->statistics.bytes_written_subrun >= log_chn->settings.subrun_byte_limit) {
02496       int run_number;
02497 
02498       // cm_msg(MTALK, "main", "stopping subrun after %1.0lf bytes", log_chn->settings.subrun_byte_limit);
02499 
02500       size = sizeof(run_number);
02501       status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
02502       assert(status == SUCCESS);
02503 
02504       stop_requested = TRUE; // avoid recursive call thourgh log_odb_dump
02505       log_close(log_chn, run_number);
02506       log_chn->subrun_number++;
02507       log_chn->statistics.bytes_written_subrun = 0;
02508       log_generate_file_name(log_chn);
02509       log_open(log_chn, run_number);
02510       subrun_start_time = ss_time();
02511       stop_requested = FALSE;
02512    }
02513 
02514    /* check if byte limit is reached to stop run */
02515    if (!stop_requested && !in_stop_transition &&
02516        log_chn->settings.byte_limit > 0 &&
02517        log_chn->statistics.bytes_written >= log_chn->settings.byte_limit) {
02518       stop_requested = TRUE;
02519 
02520       cm_msg(MTALK, "log_write", "stopping run after having received %1.0lf mega bytes",
02521              log_chn->statistics.bytes_written / 1E6);
02522 
02523       status = stop_the_run(1);
02524 
02525       return status;
02526    }
02527 
02528    /* check if capacity is reached for tapes */
02529    if (!stop_requested && !in_stop_transition &&
02530        log_chn->type == LOG_TYPE_TAPE &&
02531        log_chn->settings.tape_capacity > 0 &&
02532        log_chn->statistics.bytes_written_total >= log_chn->settings.tape_capacity) {
02533       stop_requested = TRUE;
02534       cm_msg(MTALK, "log_write", "tape capacity reached, stopping run");
02535 
02536       /* remember tape name */
02537       strcpy(tape_name, log_chn->path);
02538       stats_hkey = log_chn->stats_hkey;
02539 
02540       status = stop_the_run(0);
02541 
02542       /* rewind tape */
02543       ss_tape_open(tape_name, O_RDONLY, &htape);
02544       cm_msg(MTALK, "log_write", "rewinding tape %s, please wait", log_chn->path);
02545 
02546       cm_get_watchdog_params(&watchdog_flag, &watchdog_timeout);
02547       cm_set_watchdog_params(watchdog_flag, 300000);    /* 5 min for tape rewind */
02548       ss_tape_unmount(htape);
02549       ss_tape_close(htape);
02550       cm_set_watchdog_params(watchdog_flag, watchdog_timeout);
02551 
02552       /* zero statistics */
02553       dzero = izero = 0;
02554       db_set_value(hDB, stats_hkey, "Bytes written total", &dzero, sizeof(dzero), 1, TID_DOUBLE);
02555       db_set_value(hDB, stats_hkey, "Files written", &izero, sizeof(izero), 1, TID_INT);
02556 
02557       cm_msg(MTALK, "log_write", "Please insert new tape and start new run.");
02558 
02559       return status;
02560    }
02561 
02562    /* stop run if less than 10MB free disk space */
02563    actual_time = ss_millitime();
02564    if (log_chn->type == LOG_TYPE_DISK && actual_time - last_checked > 1000) {
02565       last_checked = actual_time;
02566 
02567       if (ss_disk_free(log_chn->path) < 1E7 && !stop_requested && !in_stop_transition) {
02568          stop_requested = TRUE;
02569          cm_msg(MTALK, "log_write", "disk nearly full, stopping run");
02570 
02571          status = stop_the_run(0);
02572       }
02573    }
02574 
02575    return status;
02576 }
02577 
02578 /*---- open_history ------------------------------------------------*/
02579 
02580 void log_history(HNDLE hDB, HNDLE hKey, void *info);
02581 
02582 #include "history.h"
02583 
02584 static std::vector<MidasHistoryInterface*> mh;
02585 
02586 static int add_event(int* indexp, int event_id, const char* event_name, HNDLE hKey, int ntags, const TAG* tags, int period, int hotlink)
02587 {
02588    int status;
02589    int size, i;
02590    int oldTags = 0;
02591    int disableTags = 0;
02592    int index = *indexp;
02593 
02594    if (0) {   
02595       /* print the tags */
02596       printf("add_event: event %d, name \"%s\", ntags %d\n", event_id, event_name, ntags);
02597       for (i=0; i<ntags; i++) {
02598          printf("tag %d: name \"%s\", type %d, n_data %d\n", i, tags[i].name, tags[i].type, tags[i].n_data);
02599       }
02600    }
02601 
02602 
02603    /* check for duplicate event id's */
02604    for (i=0; i<index; i++) {
02605       if (hist_log[i].event_id == event_id) {
02606          cm_msg(MERROR, "add_event", "Duplicate event id %d for \'%s\'", event_id, event_name);
02607          return 0;
02608       }
02609       if (strcmp(hist_log[i].event_name, event_name) == 0) {
02610          cm_msg(MERROR, "add_event", "Duplicate event name \'%s\' with event id %d", event_name, event_id);
02611          return 0;
02612       }
02613    }
02614 
02615    while (index >= hist_log_size) {
02616       int new_size = 2*hist_log_size;
02617 
02618       if (hist_log_size == 0)
02619          new_size = 10;
02620 
02621       hist_log = (hist_log_s*)realloc(hist_log, sizeof(hist_log[0])*new_size);
02622       assert(hist_log!=NULL);
02623 
02624       hist_log_size = new_size;
02625    }
02626 
02627    if (index >= hist_log_max)
02628       hist_log_max = index + 1;
02629 
02630    /* check for invalid history tags */
02631    for (i=0; i<ntags; i++) {
02632       if (tags[i].type == TID_STRING) {
02633          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);
02634          return 0;
02635       }
02636       if (rpc_tid_size(tags[i].type) == 0) {
02637          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);
02638          return 0;
02639       }
02640    }
02641 
02642    /* check for trailing spaces in tag names */
02643    for (i=0; i<ntags; i++) {
02644       if (isspace(tags[i].name[strlen(tags[i].name)-1])) {
02645          cm_msg(MERROR, "add_event", "Invalid tag %d \'%s\' in event %d \'%s\': has trailing spaces", i, tags[i].name, event_id, event_name);
02646          return 0;
02647       }
02648    }
02649    
02650    for (unsigned i=0; i<mh.size(); i++) {
02651       status = mh[i]->hs_define_event(event_name, ntags, tags);
02652       if (status != HS_SUCCESS) {
02653          cm_msg(MERROR, "add_event", "Cannot define event \"%s\", hs_define_event() status %d", event_name, status);
02654          return 0;
02655       }
02656    }
02657 
02658    status = hs_define_event(event_id, (char*)event_name, (TAG*)tags, sizeof(TAG) * ntags);
02659    assert(status == DB_SUCCESS);
02660 
02661    status = db_get_record_size(hDB, hKey, 0, &size);
02662    assert(status == DB_SUCCESS);
02663 
02664    /* setup hist_log structure for this event */
02665    strlcpy(hist_log[index].event_name, event_name, sizeof(hist_log[index].event_name));
02666    hist_log[index].event_id    = event_id;
02667    hist_log[index].n_var       = ntags;
02668    hist_log[index].hKeyVar     = hKey;
02669    hist_log[index].buffer_size = size;
02670    hist_log[index].buffer      = (char*)malloc(size);
02671    hist_log[index].period      = period;
02672    hist_log[index].last_log    = 0;
02673 
02674    if (hist_log[index].buffer == NULL) {
02675       cm_msg(MERROR, "add_event", "Cannot allocate data buffer for event \"%s\" size %d", event_name, size);
02676       return 0;
02677    }
02678    
02679    /* open hot link to variables */
02680    if (hotlink) {
02681       status = db_open_record(hDB, hKey, hist_log[index].buffer,
02682                               size, MODE_READ, log_history, NULL);
02683       if (status != DB_SUCCESS) {
02684          cm_msg(MERROR, "add_event",
02685                 "Cannot hotlink event %d \"%s\" for history logging, db_open_record() status %d",
02686                 event_id, event_name, status);
02687          return status;
02688       }
02689    }
02690    
02691    if (verbose)
02692       printf("Created event %d for equipment \"%s\", %d tags, size %d\n", event_id, event_name, ntags, size);
02693 
02694    /* create history tags for mhttpd */
02695 
02696    disableTags = 0;
02697    size = sizeof(disableTags);
02698    status = db_get_value(hDB, 0, "/History/DisableTags", &disableTags, &size, TID_BOOL, TRUE);
02699 
02700    oldTags = 0;
02701    size = sizeof(oldTags);
02702    status = db_get_value(hDB, 0, "/History/CreateOldTags", &oldTags, &size, TID_BOOL, FALSE);
02703 
02704    if (disableTags) {
02705       HNDLE hKey;
02706 
02707       status = db_find_key(hDB, 0, "/History/Tags", &hKey);
02708       if (status == DB_SUCCESS) {
02709          status = db_delete_key(hDB, hKey, FALSE);
02710          if (status != DB_SUCCESS)
02711             cm_msg(MERROR, "add_event", "Cannot delete /History/Tags, db_delete_key() status %d", status);
02712       }
02713 
02714    } else if (oldTags) {
02715 
02716       char buf[256];
02717 
02718       sprintf(buf, "/History/Tags/%d", event_id);
02719 
02720       //printf("Set tag \'%s\' = \'%s\'\n", buf, event_name);
02721 
02722       status = db_set_value(hDB, 0, buf, (void*)event_name, strlen(event_name)+1, 1, TID_STRING);
02723       assert(status == DB_SUCCESS);
02724 
02725       for (i=0; i<ntags; i++) {
02726          WORD v = (WORD) tags[i].n_data;
02727          sprintf(buf, "/History/Tags/Tags %d/%s", event_id, tags[i].name);
02728 
02729          //printf("Set tag \'%s\' = %d\n", buf, v);
02730 
02731          status = db_set_value(hDB, 0, buf, &v, sizeof(v), 1, TID_WORD);
02732          assert(status == DB_SUCCESS);
02733 
02734          if (strlen(tags[i].name) == NAME_LENGTH-1)
02735             cm_msg(MERROR, "add_event",
02736                    "Tag name \'%s\' in event %d (%s) may have been truncated to %d characters",
02737                    tags[i].name, event_id, event_name, NAME_LENGTH-1);
02738       }
02739 
02740    } else {
02741 
02742       const int kLength = 32 + NAME_LENGTH + NAME_LENGTH;
02743       char buf[kLength];
02744       HNDLE hKey;
02745 
02746       sprintf(buf, "/History/Tags/%d", event_id);
02747       status = db_find_key(hDB, 0, buf, &hKey);
02748 
02749       if (status == DB_SUCCESS) {
02750          // add new tags
02751          KEY key;
02752 
02753          status = db_get_key(hDB, hKey, &key);
02754          assert(status == DB_SUCCESS);
02755 
02756          assert(key.type == TID_STRING);
02757 
02758          if (key.item_size < kLength && key.num_values == 1) {
02759             // old style tags are present. Convert them to new style!
02760 
02761             HNDLE hTags;
02762 
02763             cm_msg(MINFO, "add_event", "Converting old event %d (%s) tags to new style", event_id, event_name);
02764 
02765             status = db_set_data(hDB, hKey, event_name, kLength, 1, TID_STRING);
02766             assert(status == DB_SUCCESS);
02767 
02768             sprintf(buf, "/History/Tags/Tags %d", event_id);
02769 
02770             status = db_find_key(hDB, 0, buf, &hTags);
02771 
02772             if (status == DB_SUCCESS) {
02773                for (i=0; ; i++) {
02774                   HNDLE h;
02775                   int size;
02776                   KEY key;
02777                   WORD w;
02778 
02779                   status = db_enum_key(hDB, hTags, i, &h);
02780                   if (status == DB_NO_MORE_SUBKEYS)
02781                      break;
02782                   assert(status == DB_SUCCESS);
02783 
02784                   status = db_get_key(hDB, h, &key);
02785 
02786                   size = sizeof(w);
02787                   status = db_get_data(hDB, h, &w, &size, TID_WORD);
02788                   assert(status == DB_SUCCESS);
02789 
02790                   sprintf(buf, "%d[%d] %s", 0, w, key.name);
02791                   
02792                   status = db_set_data_index(hDB, hKey, buf, kLength, 1+i, TID_STRING);
02793                   assert(status == DB_SUCCESS);
02794                }
02795 
02796                status = db_delete_key(hDB, hTags, TRUE);
02797                assert(status == DB_SUCCESS);
02798             }
02799 
02800             // format conversion has changed the key, get it again
02801             status = db_get_key(hDB, hKey, &key);
02802             assert(status == DB_SUCCESS);
02803          }
02804 
02805          if (1) {
02806             // add new tags
02807          
02808             int size = key.item_size * key.num_values;
02809             int num = key.num_values;
02810 
02811             char* s = (char*)malloc(size);
02812             assert(s != NULL);
02813 
02814             TAG* t = (TAG*)malloc(sizeof(TAG)*(key.num_values + ntags));
02815             assert(t != NULL);
02816 
02817             status = db_get_data(hDB, hKey, s, &size, TID_STRING);
02818             assert(status == DB_SUCCESS);
02819 
02820             for (i=1; i<key.num_values; i++) {
02821                char* ss = s + i*key.item_size;
02822 
02823                t[i].type = 0;
02824                t[i].n_data = 0;
02825                t[i].name[0] = 0;
02826 
02827                if (isdigit(ss[0])) {
02828                   //sscanf(ss, "%d[%d] %s", &t[i].type, &t[i].n_data, t[i].name);
02829 
02830                   t[i].type = strtoul(ss, &ss, 0);
02831                   assert(*ss == '[');
02832                   ss++;
02833                   t[i].n_data = strtoul(ss, &ss, 0);
02834                   assert(*ss == ']');
02835                   ss++;
02836                   assert(*ss == ' ');
02837                   ss++;
02838                   strlcpy(t[i].name, ss, sizeof(t[i].name));
02839 
02840                   //printf("type %d, n_data %d, name [%s]\n", t[i].type, t[i].n_data, t[i].name);
02841                }
02842             }
02843 
02844             for (i=0; i<ntags; i++) {
02845                int j;
02846                int k = 0;
02847 
02848                for (j=1; j<key.num_values; j++) {
02849                   if (equal_ustring((char*)tags[i].name, (char*)t[j].name)) {
02850                      if ((tags[i].type!=t[j].type) || (tags[i].n_data!=t[j].n_data)) {
02851                         cm_msg(MINFO, "add_event", "Event %d (%s) tag \"%s\" type and size changed from %d[%d] to %d[%d]",
02852                                event_id, event_name,
02853                                tags[i].name,
02854                                t[j].type, t[j].n_data,
02855                                tags[i].type, tags[i].n_data);
02856                         k = j;
02857                         break;
02858                      }
02859 
02860                      k = -1;
02861                      break;
02862                   }
02863                }
02864 
02865                // if tag not present, k==0, so append it to the array
02866 
02867                if (k==0)
02868                   k = num;
02869 
02870                if (k > 0) {
02871                   sprintf(buf, "%d[%d] %s", tags[i].type, tags[i].n_data, tags[i].name);
02872 
02873                   status = db_set_data_index(hDB, hKey, buf, kLength, k, TID_STRING);
02874                   assert(status == DB_SUCCESS);
02875 
02876                   if (k >= num)
02877                      num = k+1;
02878                }
02879             }
02880 
02881             free(s);
02882             free(t);
02883          }
02884 
02885       } else if (status == DB_NO_KEY) {
02886          // create new array of tags
02887          status = db_create_key(hDB, 0, buf, TID_STRING);
02888          assert(status == DB_SUCCESS);
02889 
02890          status = db_find_key(hDB, 0, buf, &hKey);
02891          assert(status == DB_SUCCESS);
02892 
02893          status = db_set_data(hDB, hKey, event_name, kLength, 1, TID_STRING);
02894          assert(status == DB_SUCCESS);
02895 
02896          for (i=0; i<ntags; i++) {
02897             sprintf(buf, "%d[%d] %s", tags[i].type, tags[i].n_data, tags[i].name);
02898 
02899             status = db_set_data_index(hDB, hKey, buf, kLength, 1+i, TID_STRING);
02900             assert(status == DB_SUCCESS);
02901          }
02902       } else {
02903          cm_msg(MERROR, "add_event", "Error: db_find_key(%s) status %d", buf, status);
02904          return 0;
02905       }
02906    }
02907 
02908    *indexp = index+1;
02909 
02910    return SUCCESS;
02911 }
02912 
02913 static int get_event_id(int eq_id, const char* eq_name, const char* var_name)
02914 {
02915    HNDLE hDB, hKeyRoot;
02916    char name[NAME_LENGTH+NAME_LENGTH+2];
02917    int status, i;
02918    WORD max_id = 0;
02919 
02920    strlcpy(name, eq_name, sizeof(name));
02921    strlcat(name, ":", sizeof(name));
02922    strlcat(name, var_name, sizeof(name));
02923 
02924    //printf("Looking for event id for \'%s\'\n", name);
02925 
02926    cm_get_experiment_database(&hDB, NULL);
02927    
02928    status = db_find_key(hDB, 0, "/History/Events", &hKeyRoot);
02929    if (status == DB_SUCCESS) {
02930       for (i = 0;; i++) {
02931          HNDLE hKey;
02932          KEY key;
02933          WORD evid;
02934          int size;
02935          char tmp[NAME_LENGTH+NAME_LENGTH+2];
02936          
02937          status = db_enum_key(hDB, hKeyRoot, i, &hKey);
02938          if (status != DB_SUCCESS)
02939            break;
02940          
02941          status = db_get_key(hDB, hKey, &key);
02942          assert(status == DB_SUCCESS);
02943          
02944          //printf("key \'%s\'\n", key.name);
02945          
02946          evid = (WORD) strtol(key.name, NULL, 0);
02947          if (evid == 0)
02948             continue;
02949 
02950          size = sizeof(tmp);
02951          status = db_get_data(hDB, hKey, tmp, &size, TID_STRING);
02952          //printf("status %d\n", status);
02953          assert(status == DB_SUCCESS);
02954 
02955          //printf("got %d \'%s\' looking for \'%s\'\n", evid, tmp, name);
02956 
02957          if (equal_ustring(name, tmp))
02958             return evid;
02959 
02960          if (evid/1000 == eq_id)
02961             max_id = evid;
02962       }
02963    }
02964 
02965    //printf("eq_id %d, max_id %d\n", eq_id, max_id);
02966 
02967    if (max_id == 0)
02968       max_id = eq_id * 1000;
02969 
02970    if (max_id < 1000)
02971       max_id = 1000;
02972 
02973    while (1) {
02974       char tmp[NAME_LENGTH+NAME_LENGTH+2];
02975       HNDLE hKey;
02976       WORD evid = max_id + 1;
02977 
02978       sprintf(tmp,"/History/Events/%d", evid);
02979 
02980       status = db_find_key(hDB, 0, tmp, &hKey);
02981       if (status == DB_SUCCESS) {
02982          max_id = evid;
02983          assert(max_id < 65000);
02984          continue;
02985       }
02986 
02987       status = db_set_value(hDB, 0, tmp, name, strlen(name)+1, 1, TID_STRING);
02988       assert(status == DB_SUCCESS);
02989 
02990       return evid;
02991    }
02992 
02993    /* not reached */
02994    return 0;
02995 }
02996 
02997 INT open_history()
02998 {
02999    INT size, index, i_tag, status, i, j, li, max_event_id;
03000    int ieq;
03001    INT n_var, n_tags, n_names = 0;
03002    HNDLE hKeyRoot, hKeyVar, hKeyNames, hLinkKey, hVarKey, hKeyEq, hHistKey, hKey;
03003    DWORD history;
03004    TAG *tag = NULL;
03005    KEY key, varkey, linkkey, histkey;
03006    WORD eq_id;
03007    char str[256], eq_name[NAME_LENGTH], hist_name[NAME_LENGTH];
03008    BOOL single_names;
03009    int count_events = 0;
03010    int global_per_variable_history = 0;
03011 
03012    for (unsigned i=0; i<mh.size(); i++)
03013       delete mh[i];
03014    mh.clear();
03015 
03016 #ifdef HAVE_ODBC
03017    int debug = 0;
03018    size = sizeof(debug);
03019    status = db_get_value(hDB, 0, "/Logger/ODBC_Debug", &debug, &size, TID_INT, TRUE);
03020    assert(status==DB_SUCCESS);
03021 
03022    /* check ODBC connection */
03023    char dsn[256];
03024    size = sizeof(dsn);
03025    dsn[0] = 0;
03026 
03027    status = db_get_value(hDB, 0, "/Logger/ODBC_DSN", dsn, &size, TID_STRING, TRUE);
03028    assert(status==DB_SUCCESS);
03029 
03030    if (debug == 2) {
03031 
03032       MidasHistoryInterface* hi = MakeMidasHistorySqlDebug();
03033       assert(hi);
03034 
03035       hi->hs_set_debug(debug);
03036       
03037       status = hi->hs_connect(dsn);
03038       if (status != HS_SUCCESS) {
03039          cm_msg(MERROR, "open_history", "Cannot connect to SQL debug driver \'%s\', status %d", dsn, status);
03040          return status;
03041       }
03042 
03043       mh.push_back(hi);
03044 
03045    } else if (strlen(dsn)>1 && dsn[0]!='#') {
03046 
03047       MidasHistoryInterface* hi = MakeMidasHistoryODBC();
03048       assert(hi);
03049 
03050       hi->hs_set_debug(debug);
03051       
03052       status = hi->hs_connect(dsn);
03053       if (status != HS_SUCCESS) {
03054          cm_msg(MERROR, "open_history", "Cannot connect to ODBC database with DSN \'%s\', status %d", dsn, status);
03055          return status;
03056       }
03057 
03058       mh.push_back(hi);
03059    }
03060 #endif
03061 
03062    for (unsigned i=0; i<mh.size(); i++) {
03063       status = mh[i]->hs_clear_cache();
03064       assert(status == HS_SUCCESS);
03065    }
03066 
03067    /* set directory for history files */
03068    size = sizeof(str);
03069    str[0] = 0;
03070    status = db_get_value(hDB, 0, "/Logger/History Dir", str, &size, TID_STRING, FALSE);
03071    if (status != DB_SUCCESS)
03072       db_get_value(hDB, 0, "/Logger/Data Dir", str, &size, TID_STRING, TRUE);
03073 
03074    if (str[0] != 0)
03075       hs_set_path(str);
03076 
03077    if (db_find_key(hDB, 0, "/History/Links", &hKeyRoot) != DB_SUCCESS ||
03078        db_find_key(hDB, 0, "/History/Links/System", &hKeyRoot) != DB_SUCCESS) {
03079       /* create default history keys */
03080       db_create_key(hDB, 0, "/History/Links", TID_KEY);
03081 
03082       if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/Events per sec.", &hKeyEq) == DB_SUCCESS)
03083          db_create_link(hDB, 0, "/History/Links/System/Trigger per sec.",
03084                         "/Equipment/Trigger/Statistics/Events per sec.");
03085 
03086       if (db_find_key(hDB, 0, "/Equipment/Trigger/Statistics/kBytes per sec.", &hKeyEq) == DB_SUCCESS)
03087          db_create_link(hDB, 0, "/History/Links/System/Trigger kB per sec.",
03088                         "/Equipment/Trigger/Statistics/kBytes per sec.");
03089    }
03090 
03091    /*---- define equipment events as history ------------------------*/
03092 
03093    max_event_id = 0;
03094 
03095    status = db_find_key(hDB, 0, "/Equipment", &hKeyRoot);
03096    if (status != DB_SUCCESS) {
03097       cm_msg(MERROR, "open_history", "Cannot find Equipment entry in database");
03098       return 0;
03099    }
03100 
03101    size = sizeof(int);
03102    status = db_get_value(hDB, 0, "/History/PerVariableHistory", &global_per_variable_history, &size, TID_INT, TRUE);
03103    assert(status==DB_SUCCESS);
03104 
03105    /* loop over equipment */
03106    index = 0;
03107    for (ieq = 0; ; ieq++) {
03108       status = db_enum_key(hDB, hKeyRoot, ieq, &hKeyEq);
03109       if (status != DB_SUCCESS)
03110          break;
03111 
03112       /* check history flag */
03113       size = sizeof(history);
03114       db_get_value(hDB, hKeyEq, "Common/Log history", &history, &size, TID_INT, TRUE);
03115 
03116       /* define history tags only if log history flag is on */
03117       if (history > 0) {
03118          BOOL per_variable_history = global_per_variable_history;
03119 
03120          /* get equipment name */
03121          db_get_key(hDB, hKeyEq, &key);
03122          strcpy(eq_name, key.name);
03123 
03124          if (strchr(eq_name, ':'))
03125             cm_msg(MERROR, "open_history", "Equipment name \'%s\' contains characters \':\', this may break the history system", eq_name);
03126 
03127          status = db_find_key(hDB, hKeyEq, "Variables", &hKeyVar);
03128          if (status != DB_SUCCESS) {
03129             cm_msg(MERROR, "open_history", "Cannot find /Equipment/%s/Variables entry in database", eq_name);
03130             return 0;
03131          }
03132 
03133          size = sizeof(eq_id);
03134          status = db_get_value(hDB, hKeyEq, "Common/Event ID", &eq_id, &size, TID_WORD, TRUE);
03135          assert(status == DB_SUCCESS);
03136 
03137          size = sizeof(int);
03138          status = db_get_value(hDB, hKeyEq, "Settings/PerVariableHistory", &per_variable_history, &size, TID_INT, FALSE);
03139          assert(status == DB_SUCCESS || status == DB_NO_KEY);
03140 
03141          if (verbose)
03142             printf
03143                 ("\n==================== Equipment \"%s\", ID %d  =======================\n",
03144                  eq_name, eq_id);
03145 
03146          /* count keys in variables tree */
03147          for (n_var = 0, n_tags = 0;; n_var++) {
03148             status = db_enum_key(hDB, hKeyVar, n_var, &hKey);
03149             if (status == DB_NO_MORE_SUBKEYS)
03150                break;
03151             db_get_key(hDB, hKey, &key);
03152             if (key.type != TID_KEY) {
03153                n_tags += key.num_values;
03154             }
03155             else {
03156                int ii;
03157                for (ii=0;; ii++) {
03158                   KEY vvarkey;
03159                   HNDLE hhKey;
03160 
03161                   status = db_enum_key(hDB, hKey, ii, &hhKey);
03162                   if (status == DB_NO_MORE_SUBKEYS)
03163                      break;
03164 
03165                   /* get variable key */
03166                   db_get_key(hDB, hhKey, &vvarkey);
03167 
03168                   n_tags += vvarkey.num_values;
03169                }
03170             }
03171          }
03172 
03173          if (n_var == 0)
03174             cm_msg(MERROR, "open_history", "defined event %d with no variables in ODB", eq_id);
03175 
03176          /* create tag array */
03177          tag = (TAG *) malloc(sizeof(TAG) * n_tags);
03178  
03179          i_tag = 0;
03180          for (i=0; ; i++) {
03181             status = db_enum_key(hDB, hKeyVar, i, &hKey);
03182             if (status == DB_NO_MORE_SUBKEYS)
03183                break;
03184 
03185             /* get variable key */
03186             db_get_key(hDB, hKey, &varkey);
03187 
03188             /* look for names */
03189             db_find_key(hDB, hKeyEq, "Settings/Names", &hKeyNames);
03190             single_names = (hKeyNames > 0);
03191             if (hKeyNames) {
03192                if (verbose)
03193                   printf("Using \"/Equipment/%s/Settings/Names\" for variable \"%s\"\n",
03194                          eq_name, varkey.name);
03195 
03196                /* define tags from names list */
03197                db_get_key(hDB, hKeyNames, &key);
03198                n_names = key.num_values;
03199             } else {
03200                sprintf(str, "Settings/Names %s", varkey.name);
03201                db_find_key(hDB, hKeyEq, str, &hKeyNames);
03202                if (hKeyNames) {
03203                   if (verbose)
03204                      printf
03205                          ("Using \"/Equipment/%s/Settings/Names %s\" for variable \"%s\"\n",
03206                           eq_name, varkey.name, varkey.name);
03207 
03208                   /* define tags from names list */
03209                   db_get_key(hDB, hKeyNames, &key);
03210                   n_names = key.num_values;
03211                }
03212             }
03213 
03214             if (hKeyNames && n_names < varkey.num_values) {
03215                cm_msg(MERROR, "open_history",
03216                       "Array size mismatch: \"/Equipment/%s/Settings/%s\" has %d entries while \"/Equipment/%s/Variables/%s\" has %d entries",
03217                       eq_name, key.name, n_names,
03218                       eq_name, varkey.name, varkey.num_values);
03219                free(tag);
03220                return 0;
03221             }
03222 
03223             if (hKeyNames) {
03224                /* loop over array elements */
03225                for (j = 0; j < varkey.num_values; j++) {
03226                   char xname[256];
03227 
03228                   tag[i_tag].name[0] = 0;
03229 
03230                   /* get name #j */
03231                   size = sizeof(xname);
03232                   status = db_get_data_index(hDB, hKeyNames, xname, &size, j, TID_STRING);
03233                   if (status == DB_SUCCESS)
03234                      strlcpy(tag[i_tag].name, xname, sizeof(tag[i_tag].name));
03235 
03236                   if (strlen(tag[i_tag].name) < 1) {
03237                      char buf[256];
03238                      sprintf(buf, "%d", j);
03239                      strlcpy(tag[i_tag].name, varkey.name, NAME_LENGTH);
03240                      strlcat(tag[i_tag].name, "_", NAME_LENGTH);
03241                      strlcat(tag[i_tag].name, buf, NAME_LENGTH);
03242                   }
03243 
03244                   /* append variable key name for single name array */
03245                   if (single_names) {
03246                      if (strlen(tag[i_tag].name) + 1 + strlen(varkey.name) >= NAME_LENGTH) {
03247                         cm_msg(MERROR, "open_history",
03248                                "Name for history entry \"%s %s\" too long", tag[i_tag].name, varkey.name);
03249                         free(tag);
03250                         return 0;
03251                      }
03252                      strlcat(tag[i_tag].name, " ", NAME_LENGTH);
03253                      strlcat(tag[i_tag].name, varkey.name, NAME_LENGTH);
03254                   }
03255 
03256                   tag[i_tag].type = varkey.type;
03257                   tag[i_tag].n_data = 1;
03258 
03259                   if (verbose)
03260                      printf("Defined tag %d, name \"%s\", type %d, num_values %d\n",
03261                             i_tag, tag[i_tag].name, tag[i_tag].type, tag[i_tag].n_data);
03262 
03263                   i_tag++;
03264                }
03265             } else if (varkey.type == TID_KEY) {
03266                int ii;
03267                for (ii=0;; ii++) {
03268                   KEY vvarkey;
03269                   HNDLE hhKey;
03270 
03271                   status = db_enum_key(hDB, hKey, ii, &hhKey);
03272                   if (status == DB_NO_MORE_SUBKEYS)
03273                      break;
03274 
03275                   /* get variable key */
03276                   db_get_key(hDB, hhKey, &vvarkey);
03277 
03278                   strlcpy(tag[i_tag].name, varkey.name, NAME_LENGTH);
03279                   strlcat(tag[i_tag].name, "_", NAME_LENGTH);
03280                   strlcat(tag[i_tag].name, vvarkey.name, NAME_LENGTH);
03281                   tag[i_tag].type = vvarkey.type;
03282                   tag[i_tag].n_data = vvarkey.num_values;
03283 
03284                   if (verbose)
03285                      printf("Defined tag %d, name \"%s\", type %d, num_values %d\n", i_tag, tag[i_tag].name,
03286                             tag[i_tag].type, tag[i_tag].n_data);
03287 
03288                   i_tag++;
03289                }
03290             } else {
03291                strlcpy(tag[i_tag].name, varkey.name, NAME_LENGTH);
03292                tag[i_tag].type = varkey.type;
03293                tag[i_tag].n_data = varkey.num_values;
03294 
03295                if (verbose)
03296                   printf("Defined tag %d, name \"%s\", type %d, num_values %d\n", i_tag, tag[i_tag].name,
03297                          tag[i_tag].type, tag[i_tag].n_data);
03298 
03299                i_tag++;
03300             }
03301 
03302             if (per_variable_history && i_tag>0) {
03303                WORD event_id;
03304                char event_name[NAME_LENGTH];
03305 
03306                event_id = get_event_id(eq_id, eq_name, varkey.name);
03307                assert(event_id > 0);
03308 
03309                strlcpy(event_name, eq_name, NAME_LENGTH);
03310                strlcat(event_name, "/", NAME_LENGTH);
03311                strlcat(event_name, varkey.name, NAME_LENGTH);
03312 
03313                assert(i_tag <= n_tags);
03314 
03315                status = add_event(&index, event_id, event_name, hKey, i_tag, tag, history, 1);
03316                if (status != DB_SUCCESS)
03317                   return status;
03318 
03319                count_events++;
03320 
03321                i_tag = 0;
03322             } /* if per-variable history */
03323 
03324          } /* loop over variables */
03325 
03326          if (!per_variable_history && i_tag>0) {
03327             assert(i_tag <= n_tags);
03328 
03329             status = add_event(&index, eq_id, eq_name, hKeyVar, i_tag, tag, history, 1);
03330             if (status != DB_SUCCESS)
03331                return status;
03332 
03333             count_events++;
03334          }
03335 
03336          if (tag)
03337             free(tag);
03338 
03339          /* remember maximum event id for later use with system events */
03340          if (eq_id > max_event_id)
03341             max_event_id = eq_id;
03342       }
03343    } /* loop over equipments */
03344 
03345    /*---- define linked trees ---------------------------------------*/
03346 
03347    /* round up event id */
03348    max_event_id = ((int) ((max_event_id + 1) / 10) + 1) * 10;
03349 
03350    status = db_find_key(hDB, 0, "/History/Links", &hKeyRoot);
03351    if (status == DB_SUCCESS) {
03352       for (li = 0;; li++) {
03353          status = db_enum_link(hDB, hKeyRoot, li, &hHistKey);
03354          if (status == DB_NO_MORE_SUBKEYS)
03355             break;
03356 
03357          db_get_key(hDB, hHistKey, &histkey);
03358          strcpy(hist_name, histkey.name);
03359          db_enum_key(hDB, hKeyRoot, li, &hHistKey);
03360 
03361          db_get_key(hDB, hHistKey, &key);
03362          if (key.type != TID_KEY) {
03363             cm_msg(MERROR, "open_history", "Only subkeys allows in /history/links");
03364             continue;
03365          }
03366 
03367          if (verbose)
03368             printf
03369                 ("\n==================== History link \"%s\", ID %d  =======================\n",
03370                  hist_name, max_event_id);
03371 
03372          /* count subkeys in link */
03373          for (i = n_var = 0;; i++) {
03374             status = db_enum_key(hDB, hHistKey, i, &hKey);
03375             if (status == DB_NO_MORE_SUBKEYS)
03376                break;
03377 
03378             if (status == DB_SUCCESS && db_get_key(hDB, hKey, &key) == DB_SUCCESS) {
03379                if (key.type != TID_KEY)
03380                   n_var++;
03381             } else {
03382                db_enum_link(hDB, hHistKey, i, &hKey);
03383                db_get_key(hDB, hKey, &key);
03384                cm_msg(MERROR, "open_history",
03385                       "History link /History/Links/%s/%s is invalid", hist_name, key.name);
03386                return 0;
03387             }
03388          }
03389 
03390          if (n_var == 0)
03391             cm_msg(MERROR, "open_history", "History event %s has no variables in ODB", hist_name);
03392          else {
03393             /* create tag array */
03394             tag = (TAG *) malloc(sizeof(TAG) * n_var);
03395 
03396             for (i = 0, size = 0, n_var = 0;; i++) {
03397                status = db_enum_link(hDB, hHistKey, i, &hLinkKey);
03398                if (status == DB_NO_MORE_SUBKEYS)
03399                   break;
03400 
03401                /* get link key */
03402                db_get_key(hDB, hLinkKey, &linkkey);
03403 
03404                if (linkkey.type == TID_KEY)
03405                   continue;
03406 
03407                /* get link target */
03408                db_enum_key(hDB, hHistKey, i, &hVarKey);
03409                if (db_get_key(hDB, hVarKey, &varkey) == DB_SUCCESS) {
03410                   /* hot-link individual values */
03411                   if (histkey.type == TID_KEY)
03412                      db_open_record(hDB, hVarKey, NULL, varkey.total_size, MODE_READ,
03413                                     log_system_history, (void *) (POINTER_T) index);
03414 
03415                   strcpy(tag[n_var].name, linkkey.name);
03416                   tag[n_var].type = varkey.type;
03417                   tag[n_var].n_data = varkey.num_values;
03418 
03419                   if (verbose)
03420                      printf("Defined tag \"%s\", type %d, num_values %d\n",
03421                             tag[n_var].name, tag[n_var].type, tag[n_var].n_data);
03422 
03423                   size += varkey.total_size;
03424                   n_var++;
03425                }
03426             }
03427 
03428             /* hot-link whole subtree */
03429             if (histkey.type == TID_LINK)
03430                db_open_record(hDB, hHistKey, NULL, size, MODE_READ, log_system_history,
03431                               (void *) (POINTER_T) index);
03432 
03433             status = add_event(&index, max_event_id, hist_name, hHistKey, n_var, tag, 10, 0);
03434             if (status != DB_SUCCESS)
03435                return status;
03436 
03437             free(tag);
03438 
03439             count_events++;
03440             max_event_id++;
03441          }
03442       }
03443    }
03444 
03445    /*---- define run start/stop event -------------------------------*/
03446 
03447    tag = (TAG *) malloc(sizeof(TAG) * 2);
03448 
03449    strcpy(tag[0].name, "State");
03450    tag[0].type = TID_DWORD;
03451    tag[0].n_data = 1;
03452 
03453    strcpy(tag[1].name, "Run number");
03454    tag[1].type = TID_DWORD;
03455    tag[1].n_data = 1;
03456 
03457    const char* event_name = "Run transitions";
03458 
03459    for (unsigned i=0; i<mh.size(); i++) {
03460       status = mh[i]->hs_define_event(event_name, 2, tag);
03461       if (status != HS_SUCCESS) {
03462          cm_msg(MERROR, "add_event", "Cannot define event \"%s\", hs_define_event() status %d", event_name, status);
03463          return 0;
03464       }
03465    }
03466 
03467    hs_define_event(0, (char*)event_name, tag, sizeof(TAG) * 2);
03468    free(tag);
03469 
03470    /* outcommented not to produce a log entry on every run
03471    cm_msg(MINFO, "open_history", "Configured history with %d events", count_events);
03472    */
03473 
03474    return CM_SUCCESS;
03475 }
03476 
03477 /*---- close_history -----------------------------------------------*/
03478 
03479 void close_history()
03480 {
03481    INT i, status;
03482    HNDLE hKeyRoot, hKey;
03483 
03484    /* close system history */
03485    status = db_find_key(hDB, 0, "/History/Links", &hKeyRoot);
03486    if (status != DB_SUCCESS) {
03487       for (i = 0;; i++) {
03488          status = db_enum_key(hDB, hKeyRoot, i, &hKey);
03489          if (status == DB_NO_MORE_SUBKEYS)
03490             break;
03491          db_close_record(hDB, hKey);
03492       }
03493    }
03494 
03495    /* close event history */
03496    for (i = 1; i < hist_log_max; i++)
03497       if (hist_log[i].hKeyVar) {
03498          db_close_record(hDB, hist_log[i].hKeyVar);
03499          hist_log[i].hKeyVar = 0;
03500          if (hist_log[i].buffer)
03501             free(hist_log[i].buffer);
03502          hist_log[i].buffer = NULL;
03503       }
03504 
03505    for (unsigned h=0; h<mh.size(); h++)
03506       status  = mh[h]->hs_disconnect();
03507 }
03508 
03509 /*---- log_history -------------------------------------------------*/
03510 
03511 void log_history(HNDLE hDB, HNDLE hKey, void *info)
03512 {
03513    INT i, size;
03514 
03515    for (i = 0; i < hist_log_max; i++)
03516       if (hist_log[i].hKeyVar == hKey)
03517          break;
03518 
03519    if (i == hist_log_max)
03520       return;
03521 
03522    /* check if over period */
03523    if (ss_time() - hist_log[i].last_log < hist_log[i].period)
03524       return;
03525 
03526    /* check if event size has changed */
03527    db_get_record_size(hDB, hKey, 0, &size);
03528    if (size != hist_log[i].buffer_size) {
03529       close_history();
03530       open_history();
03531       return;
03532    }
03533 
03534    hist_log[i].last_log = ss_time();
03535 
03536    if (verbose)
03537       printf("write history event: id %d, timestamp %d, buffer %p, size %d\n", hist_log[i].event_id, hist_log[i].last_log, hist_log[i].buffer, hist_log[i].buffer_size);
03538 
03539    hs_write_event(hist_log[i].event_id, hist_log[i].buffer, hist_log[i].buffer_size);
03540 
03541    for (unsigned h=0; h<mh.size(); h++) {
03542       int 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);
03543       if (verbose)
03544          if (status != HS_SUCCESS)
03545             printf("hs_write_event() status %d\n", status);
03546    }
03547 }
03548 
03549 /*------------------------------------------------------------------*/
03550 
03551 void log_system_history(HNDLE hDB, HNDLE hKey, void *info)
03552 {
03553    INT size, total_size, status, index;
03554    DWORD i;
03555    KEY key;
03556 
03557    index = (INT) (POINTER_T) info;
03558 
03559    /* check if over period */
03560    if (ss_time() - hist_log[index].last_log < hist_log[index].period)
03561       return;
03562 
03563    for (i = 0, total_size = 0;; i++) {
03564       status = db_enum_key(hDB, hist_log[index].hKeyVar, i, &hKey);
03565       if (status == DB_NO_MORE_SUBKEYS)
03566          break;
03567 
03568       db_get_key(hDB, hKey, &key);
03569       size = key.total_size;
03570       db_get_data(hDB, hKey, (char *) hist_log[index].buffer + total_size, &size, key.type);
03571       total_size += size;
03572    }
03573 
03574    if (i != hist_log[index].n_var) {
03575       close_history();
03576       open_history();
03577    } else {
03578       hist_log[index].last_log = ss_time();
03579 
03580       hs_write_event(hist_log[index].event_id, hist_log[index].buffer, hist_log[index].buffer_size);
03581 
03582       for (unsigned h=0; h<mh.size(); h++)
03583          mh[h]->hs_write_event(hist_log[index].event_name, hist_log[index].last_log, hist_log[index].buffer_size, hist_log[index].buffer);
03584    }
03585 
03586 
03587    /* simulate odb key update for hot links connected to system history */
03588    if (!rpc_is_remote()) {
03589       db_lock_database(hDB);
03590       db_notify_clients(hDB, hist_log[index].hKeyVar, FALSE);
03591       db_unlock_database(hDB);
03592    }
03593 
03594 }
03595 
03596 /*------------------------------------------------------------------*/
03597 
03598 INT log_callback(INT index, void *prpc_param[])
03599 {
03600    HNDLE hKeyRoot, hKeyChannel;
03601    INT i, status, size, channel, izero, htape, online_mode;
03602    DWORD watchdog_timeout;
03603    BOOL watchdog_flag;
03604    char str[256];
03605    double dzero;
03606 
03607    /* rewind tapes */
03608    if (index == RPC_LOG_REWIND) {
03609       channel = *((INT *) prpc_param[0]);
03610 
03611       /* loop over all channels */
03612       status = db_find_key(hDB, 0, "/Logger/Channels", &hKeyRoot);
03613       if (status != DB_SUCCESS) {
03614          cm_msg(MERROR, "log_callback", "cannot find Logger/Channels entry in database");
03615          return 0;
03616       }
03617 
03618       /* check online mode */
03619       online_mode = 0;
03620       size = sizeof(online_mode);
03621       db_get_value(hDB, 0, "/Runinfo/online mode", &online_mode, &size, TID_INT, TRUE);
03622 
03623       for (i = 0; i < MAX_CHANNELS; i++) {
03624          status = db_enum_key(hDB, hKeyRoot, i, &hKeyChannel);
03625          if (status == DB_NO_MORE_SUBKEYS)
03626             break;
03627 
03628          /* skip if wrong channel, -1 means rewind all channels */
03629          if (channel != i && channel != -1)
03630             continue;
03631 
03632          if (status == DB_SUCCESS) {
03633             size = sizeof(str);
03634             status = db_get_value(hDB, hKeyChannel, "Settings/Type", str, &size, TID_STRING, TRUE);
03635             if (status != DB_SUCCESS)
03636                continue;
03637 
03638             if (equal_ustring(str, "Tape")) {
03639                size = sizeof(str);
03640                status = db_get_value(hDB, hKeyChannel, "Settings/Filename", str, &size, TID_STRING, TRUE);
03641                if (status != DB_SUCCESS)
03642                   continue;
03643 
03644                if (ss_tape_open(str, O_RDONLY, &htape) == SS_SUCCESS) {
03645                   cm_msg(MTALK, "log_callback", "rewinding tape #%d, please wait", i);
03646 
03647                   cm_get_watchdog_params(&watchdog_flag, &watchdog_timeout);
03648                   cm_set_watchdog_params(watchdog_flag, 300000);        /* 5 min for tape rewind */
03649                   ss_tape_rewind(htape);
03650                   if (online_mode)
03651                      ss_tape_unmount(htape);
03652                   cm_set_watchdog_params(watchdog_flag, watchdog_timeout);
03653 
03654                   cm_msg(MINFO, "log_callback", "Tape %s rewound sucessfully", str);
03655                } else
03656                   cm_msg(MERROR, "log_callback", "Cannot rewind tape %s", str);
03657 
03658                ss_tape_close(htape);
03659 
03660                /* clear statistics */
03661                dzero = izero = 0;
03662                log_chn[i].statistics.bytes_written_total = 0;
03663                log_chn[i].statistics.files_written = 0;
03664                db_set_value(hDB, hKeyChannel, "Statistics/Bytes written total", &dzero,
03665                             sizeof(dzero), 1, TID_DOUBLE);
03666                db_set_value(hDB, hKeyChannel, "Statistics/Files written", &izero, sizeof(izero), 1, TID_INT);
03667             }
03668          }
03669       }
03670 
03671       cm_msg(MTALK, "log_callback", "tape rewind finished");
03672    }
03673 
03674    return RPC_SUCCESS;
03675 }
03676 
03677 /*------------------------------------------------------------------*/
03678 
03679 int log_generate_file_name(LOG_CHN *log_chn)
03680 {
03681    INT size, status, run_number;
03682    char str[256], path[256], dir[256], data_dir[256];
03683    CHN_SETTINGS *chn_settings;
03684    time_t now;
03685    struct tm *tms;
03686 
03687    chn_settings = &log_chn->settings;
03688    size = sizeof(run_number);
03689    status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
03690    assert(status == SUCCESS);
03691 
03692    data_dir[0] = 0;
03693 
03694    /* if disk, precede filename with directory if not already there */
03695    if (log_chn->type == LOG_TYPE_DISK && chn_settings->filename[0] != DIR_SEPARATOR) {
03696       size = sizeof(data_dir);
03697       dir[0] = 0;
03698       db_get_value(hDB, 0, "/Logger/Data Dir", data_dir, &size, TID_STRING, TRUE);
03699       if (data_dir[0] != 0)
03700          if (data_dir[strlen(data_dir) - 1] != DIR_SEPARATOR)
03701             strcat(data_dir, DIR_SEPARATOR_STR);
03702       strcpy(str, data_dir);
03703 
03704       /* append subdirectory if requested */
03705       if (chn_settings->subdir_format[0]) {
03706          tzset();
03707          time(&now);
03708          tms = localtime(&now);
03709 
03710          strftime(dir, sizeof(dir), chn_settings->subdir_format, tms);
03711          strcat(str, dir);
03712          strcat(str, DIR_SEPARATOR_STR);
03713       }
03714 
03715       /* create directory if needed */
03716 #ifdef OS_WINNT
03717       status = mkdir(str);
03718 #else
03719       status = mkdir(str, 0755);
03720 #endif
03721 #if !defined(HAVE_MYSQL) && !defined(OS_WINNT)  /* errno not working with mySQL lib */
03722       if (status == -1 && errno != EEXIST)
03723          cm_msg(MERROR, "log_generate_file_name", "Cannot create subdirectory %s", str);
03724 #endif
03725 
03726       strcat(str, chn_settings->filename);
03727    } else
03728       strcpy(str, chn_settings->filename);
03729 
03730    /* check if two "%" are present in filename */
03731    if (strchr(str, '%')) {
03732       if (strchr(strchr(str, '%')+1, '%')) {
03733          /* substitude first "%d" by current run number, second "%d" by subrun number */
03734          sprintf(path, str, run_number, log_chn->subrun_number);
03735       } else {
03736          /* substitue "%d" by current run number */
03737          sprintf(path, str, run_number);
03738       }
03739    } else
03740       strcpy(path, str);
03741 
03742    strcpy(log_chn->path, path);
03743 
03744    /* write back current file name to ODB */
03745    if (strncmp(path, data_dir, strlen(data_dir)) == 0)
03746       strcpy(str, path + strlen(data_dir));
03747    else
03748       strcpy(str, path);
03749    db_set_value(hDB, log_chn->settings_hkey, "Current filename", str, 256, 1, TID_STRING);
03750 
03751    return CM_SUCCESS;
03752 }
03753 
03754 /*------------------------------------------------------------------*/
03755 
03756 /********************************************************************\
03757 
03758                          transition callbacks
03759 
03760 \********************************************************************/
03761 
03762 /*------------------------------------------------------------------*/
03763 
03764 int close_channels(int run_number, BOOL* p_tape_flag)
03765 {
03766    int i;
03767    BOOL tape_flag = FALSE;
03768 
03769    for (i = 0; i < MAX_CHANNELS; i++) {
03770       if (log_chn[i].handle || log_chn[i].ftp_con) {
03771          /* generate MTALK message */
03772          if (log_chn[i].type == LOG_TYPE_TAPE && tape_message) {
03773             tape_flag = TRUE;
03774             cm_msg(MTALK, "tr_stop", "closing tape channel #%d, please wait", i);
03775          }
03776 #ifndef FAL_MAIN
03777          /* wait until buffer is empty */
03778          if (log_chn[i].buffer_handle) {
03779 #ifdef DELAYED_STOP
03780             DWORD start_time;
03781 
03782             start_time = ss_millitime();
03783             do {
03784                cm_yield(100);
03785             } while (ss_millitime() - start_time < DELAYED_STOP);
03786 #else
03787             INT n_bytes;
03788             do {
03789                bm_get_buffer_level(log_chn[i].buffer_handle, &n_bytes);
03790                if (n_bytes > 0)
03791                   cm_yield(100);
03792             } while (n_bytes > 0);
03793 #endif
03794          }
03795 #endif                          /* FAL_MAIN */
03796 
03797          /* close logging channel */
03798          log_close(&log_chn[i], run_number);
03799 
03800          /* close statistics record */
03801          db_set_record(hDB, log_chn[i].stats_hkey, &log_chn[i].statistics, sizeof(CHN_STATISTICS), 0);
03802          db_close_record(hDB, log_chn[i].stats_hkey);
03803          db_close_record(hDB, log_chn[i].settings_hkey);
03804          log_chn[i].stats_hkey = log_chn[i].settings_hkey = 0;
03805       }
03806    }
03807 
03808    if (p_tape_flag)
03809       *p_tape_flag = tape_flag;
03810 
03811    return SUCCESS;
03812 }
03813 
03814 int close_buffers()
03815 {
03816    int i;
03817 
03818    /* close buffers */
03819    for (i = 0; i < MAX_CHANNELS; i++) {
03820 #ifndef FAL_MAIN
03821       if (log_chn[i].buffer_handle) {
03822          INT j;
03823 
03824          bm_close_buffer(log_chn[i].buffer_handle);
03825          for (j = i + 1; j < MAX_CHANNELS; j++)
03826             if (log_chn[j].buffer_handle == log_chn[i].buffer_handle)
03827                log_chn[j].buffer_handle = 0;
03828       }
03829 
03830       if (log_chn[i].msg_request_id)
03831          bm_delete_request(log_chn[i].msg_request_id);
03832 #endif
03833 
03834       /* clear channel info */
03835       memset(&log_chn[i].handle, 0, sizeof(LOG_CHN));
03836    }
03837 
03838    return SUCCESS;
03839 }
03840 
03841 /*------------------------------------------------------------------*/
03842 
03843 static int write_history(DWORD transition, DWORD run_number)
03844 {
03845    DWORD eb[2];
03846    eb[0] = transition;
03847    eb[1] = run_number;
03848 
03849    hs_write_event(0, eb, sizeof(eb));
03850 
03851    time_t now = time(NULL);
03852 
03853    for (unsigned h=0; h<mh.size(); h++)
03854       mh[h]->hs_write_event("Run transitions", now, sizeof(eb), (const char*)eb);
03855 
03856    return SUCCESS;
03857 }
03858 
03859 /*------------------------------------------------------------------*/
03860 
03861 INT tr_start(INT run_number, char *error)
03862 /********************************************************************\
03863 
03864   Prestart:
03865 
03866     Loop through channels defined in /logger/channels.
03867     Neglect channels with are not active.
03868     If "filename" contains a "%", substitute it by the
03869     current run number. Open logging channel and
03870     corresponding buffer. Place a event request
03871     into the buffer.
03872 
03873 \********************************************************************/
03874 {
03875    INT size, index, status;
03876    HNDLE hKeyRoot, hKeyChannel;
03877    CHN_SETTINGS *chn_settings;
03878    KEY key;
03879    BOOL write_data, tape_flag = FALSE;
03880 
03881    if (verbose)
03882       printf("tr_start: run %d\n", run_number);
03883 
03884    /* save current ODB */
03885    odb_save("last.xml");
03886 
03887    in_stop_transition = TRUE;
03888 
03889    close_channels(run_number, NULL);
03890    close_buffers();
03891 
03892    in_stop_transition = FALSE;
03893 
03894    run_start_time = subrun_start_time = ss_time();
03895 
03896    /* read global logging flag */
03897    size = sizeof(BOOL);
03898    write_data = TRUE;
03899    db_get_value(hDB, 0, "/Logger/Write data", &write_data, &size, TID_BOOL, TRUE);
03900 
03901    /* read tape message flag */
03902    size = sizeof(tape_message);
03903    db_get_value(hDB, 0, "/Logger/Tape message", &tape_message, &size, TID_BOOL, TRUE);
03904 
03905    /* loop over all channels */
03906    status = db_find_key(hDB, 0, "/Logger/Channels", &hKeyRoot);
03907    if (status != DB_SUCCESS) {
03908       /* if no channels are defined, define at least one */
03909       status = db_create_record(hDB, 0, "/Logger/Channels/0/", strcomb(chn_settings_str));
03910       if (status != DB_SUCCESS) {
03911          strcpy(error, "Cannot create channel entry in database");
03912          cm_msg(MERROR, "tr_start", error);
03913          return 0;
03914       }
03915 
03916       status = db_find_key(hDB, 0, "/Logger/Channels", &hKeyRoot);
03917       if (status != DB_SUCCESS) {
03918          strcpy(error, "Cannot create channel entry in database");
03919          cm_msg(MERROR, "tr_start", error);
03920          return 0;
03921       }
03922    }
03923 
03924    for (index = 0; index < MAX_CHANNELS; index++) {
03925       status = db_enum_key(hDB, hKeyRoot, index, &hKeyChannel);
03926       if (status == DB_NO_MORE_SUBKEYS)
03927          break;
03928 
03929       /* correct channel record */
03930       db_get_key(hDB, hKeyChannel, &key);
03931       status = db_check_record(hDB, hKeyRoot, key.name, strcomb(chn_settings_str), TRUE);
03932       if (status != DB_SUCCESS && status != DB_OPEN_RECORD) {
03933          cm_msg(MERROR, "tr_start", "Cannot create/check channel record, status %d", status);
03934          break;
03935       }
03936 
03937       if (status == DB_SUCCESS || status == DB_OPEN_RECORD) {
03938          /* if file already open, we had an abort on the previous start. So
03939             close and delete file in order to create a new one */
03940          if (log_chn[index].handle) {
03941             log_close(&log_chn[index], run_number);
03942             if (log_chn[index].type == LOG_TYPE_DISK) {
03943                cm_msg(MINFO, "tr_start", "Deleting previous file \"%s\"", log_chn[index].path);
03944                unlink(log_chn[index].path);
03945             }
03946          }
03947 
03948          /* if FTP channel already open, don't re-open it again */
03949          if (log_chn[index].ftp_con)
03950             continue;
03951 
03952          /* save settings key */
03953          status = db_find_key(hDB, hKeyChannel, "Settings", &log_chn[index].settings_hkey);
03954          if (status != DB_SUCCESS) {
03955             strcpy(error, "Cannot find channel settings info");
03956             cm_msg(MERROR, "tr_start", error);
03957             return 0;
03958          }
03959 
03960          /* save statistics key */
03961          status = db_find_key(hDB, hKeyChannel, "Statistics", &log_chn[index].stats_hkey);
03962          if (status != DB_SUCCESS) {
03963             strcpy(error, "Cannot find channel statistics info");
03964             cm_msg(MERROR, "tr_start", error);
03965             return 0;
03966          }
03967 
03968          /* clear statistics */
03969          size = sizeof(CHN_STATISTICS);
03970          db_get_record(hDB, log_chn[index].stats_hkey, &log_chn[index].statistics, &size, 0);
03971 
03972          log_chn[index].statistics.events_written = 0;
03973          log_chn[index].statistics.bytes_written = 0;
03974          log_chn[index].statistics.bytes_written_uncompressed = 0;
03975          log_chn[index].statistics.bytes_written_subrun = 0;
03976 
03977          db_set_record(hDB, log_chn[index].stats_hkey, &log_chn[index].statistics, size, 0);
03978 
03979          /* get channel info structure */
03980          chn_settings = &log_chn[index].settings;
03981          size = sizeof(CHN_SETTINGS);
03982          status = db_get_record(hDB, log_chn[index].settings_hkey, chn_settings, &size, 0);
03983          if (status != DB_SUCCESS) {
03984             strcpy(error, "Cannot read channel info");
03985             cm_msg(MERROR, "tr_start", error);
03986             return 0;
03987          }
03988 
03989          /* don't start run if tape is full */
03990          if (log_chn[index].type == LOG_TYPE_TAPE &&
03991              chn_settings->tape_capacity > 0 &&
03992              log_chn[index].statistics.bytes_written_total >= chn_settings->tape_capacity) {
03993             strcpy(error, "Tape capacity reached. Please load new tape");
03994             cm_msg(MERROR, "tr_start", error);
03995             return 0;
03996          }
03997 
03998          /* check if active */
03999          if (!chn_settings->active || !write_data)
04000             continue;
04001 
04002          /* check for type */
04003          if (equal_ustring(chn_settings->type, "Tape"))
04004             log_chn[index].type = LOG_TYPE_TAPE;
04005          else if (equal_ustring(chn_settings->type, "FTP"))
04006             log_chn[index].type = LOG_TYPE_FTP;
04007          else if (equal_ustring(chn_settings->type, "Disk"))
04008             log_chn[index].type = LOG_TYPE_DISK;
04009          else {
04010             sprintf(error,
04011                     "Invalid channel type \"%s\", pease use \"Tape\", \"FTP\" or \"Disk\"",
04012                     chn_settings->type);
04013             cm_msg(MERROR, "tr_start", error);
04014             return 0;
04015          }
04016 
04017          /* set compression level */
04018          log_chn[index].compression = 0;
04019          size = sizeof(log_chn[index].compression);
04020          status = db_get_value(hDB, log_chn[index].settings_hkey, "Compression", &log_chn[index].compression, &size, TID_INT, FALSE);
04021          
04022          /* initialize subrun number */
04023          log_chn[index].subrun_number = 0;
04024 
04025          log_generate_file_name(&log_chn[index]);
04026 
04027          if (log_chn[index].type == LOG_TYPE_TAPE &&
04028              log_chn[index].statistics.bytes_written_total == 0 && tape_message) {
04029             tape_flag = TRUE;
04030             cm_msg(MTALK, "tr_start", "mounting tape #%d, please wait", index);
04031          }
04032 
04033          /* open logging channel */
04034          status = log_open(&log_chn[index], run_number);
04035 
04036          /* return if logging channel couldn't be opened */
04037          if (status != SS_SUCCESS) {
04038             if (status == SS_FILE_ERROR)
04039                sprintf(error, "Cannot open file \'%s\' (See messages)", log_chn[index].path);
04040             if (status == SS_FILE_EXISTS)
04041                sprintf(error, "File \'%s\' exists already, run start aborted", log_chn[index].path);
04042             if (status == SS_NO_TAPE)
04043                sprintf(error, "No tape in device \'%s\'", log_chn[index].path);
04044             if (status == SS_TAPE_ERROR)
04045                sprintf(error, "Tape error, cannot start run");
04046             if (status == SS_DEV_BUSY)
04047                sprintf(error, "Device \'%s\' used by someone else", log_chn[index].path);
04048             if (status == FTP_NET_ERROR || status == FTP_RESPONSE_ERROR)
04049                sprintf(error, "Cannot open FTP channel to \'%s\'", log_chn[index].path);
04050             if (status == SS_NO_ROOT)
04051                sprintf(error, "No ROOT support compiled into mlogger, please compile with -DHAVE_ROOT flag");
04052 
04053             if (status == SS_INVALID_FORMAT)
04054                sprintf(error,
04055                        "Invalid data format, please use \"MIDAS\", \"ASCII\", \"DUMP\" or \"ROOT\"");
04056 
04057             cm_msg(MERROR, "tr_start", error);
04058             return 0;
04059          }
04060 
04061          /* close records if open from previous run start with abort */
04062          if (log_chn[index].stats_hkey)
04063             db_close_record(hDB, log_chn[index].stats_hkey);
04064          if (log_chn[index].settings_hkey)
04065             db_close_record(hDB, log_chn[index].settings_hkey);
04066 
04067          /* open hot link to statistics tree */
04068          status =
04069              db_open_record(hDB, log_chn[index].stats_hkey, &log_chn[index].statistics,
04070                             sizeof(CHN_STATISTICS), MODE_WRITE, NULL, NULL);
04071          if (status != DB_SUCCESS)
04072             cm_msg(MERROR, "tr_start", "cannot open statistics record, probably other logger is using it");
04073 
04074          /* open hot link to settings tree */
04075          status =
04076              db_open_record(hDB, log_chn[index].settings_hkey, &log_chn[index].settings,
04077                             sizeof(CHN_SETTINGS), MODE_READ, NULL, NULL);
04078          if (status != DB_SUCCESS)
04079             cm_msg(MERROR, "tr_start",
04080                    "cannot open channel settings record, probably other logger is using it");
04081 
04082 #ifndef FAL_MAIN
04083          /* open buffer */
04084          status = bm_open_buffer(chn_settings->buffer, 2 * MAX_EVENT_SIZE, &log_chn[index].buffer_handle);
04085          if (status != BM_SUCCESS && status != BM_CREATED) {
04086             sprintf(error, "Cannot open buffer %s", chn_settings->buffer);
04087             cm_msg(MERROR, "tr_start", error);
04088             return 0;
04089          }
04090          bm_set_cache_size(log_chn[index].buffer_handle, 100000, 0);
04091 
04092          /* place event request */
04093          status = bm_request_event(log_chn[index].buffer_handle,
04094                                    (short) chn_settings->event_id,
04095                                    (short) chn_settings->trigger_mask,
04096                                    GET_ALL, &log_chn[index].request_id, receive_event);
04097 
04098          if (status != BM_SUCCESS) {
04099             sprintf(error, "Cannot place event request");
04100             cm_msg(MERROR, "tr_start", error);
04101             return 0;
04102          }
04103 
04104          /* open message buffer if requested */
04105          if (chn_settings->log_messages) {
04106             status =
04107                 bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &log_chn[index].msg_buffer_handle);
04108             if (status != BM_SUCCESS && status != BM_CREATED) {
04109                sprintf(error, "Cannot open buffer %s", MESSAGE_BUFFER_NAME);
04110                cm_msg(MERROR, "tr_start", error);
04111                return 0;
04112             }
04113 
04114             /* place event request */
04115             status = bm_request_event(log_chn[index].msg_buffer_handle,
04116                                       (short) EVENTID_MESSAGE,
04117                                       (short) chn_settings->log_messages,
04118                                       GET_ALL, &log_chn[index].msg_request_id, receive_event);
04119 
04120             if (status != BM_SUCCESS) {
04121                sprintf(error, "Cannot place event request");
04122                cm_msg(MERROR, "tr_start", error);
04123                return 0;
04124             }
04125          }
04126 #endif
04127       }
04128    }
04129 
04130    if (tape_flag && tape_message)
04131       cm_msg(MTALK, "tr_start", "tape mounting finished");
04132 
04133    /* reopen history channels if event definition has changed */
04134    close_history();
04135    status = open_history();
04136    if (status != CM_SUCCESS) {
04137       sprintf(error, "Error in history system, aborting run start");
04138       cm_msg(MERROR, "tr_start", error);
04139       return 0;
04140    }
04141 
04142    /* write transition event into history */
04143    write_history(STATE_RUNNING, run_number);
04144 
04145 
04146 #ifdef HAVE_MYSQL
04147    /* write to SQL database if requested */
04148    write_sql(TRUE);
04149 #endif
04150 
04151    local_state = STATE_RUNNING;
04152    run_start_time = subrun_start_time = ss_time();
04153 
04154    return CM_SUCCESS;
04155 }
04156 
04157 /*-- -------- ------------------------------------------------------*/
04158 
04159 INT tr_start_abort(INT run_number, char *error)
04160 {
04161    int i;
04162 
04163    if (verbose)
04164       printf("tr_start_abort: run %d\n", run_number);
04165 
04166    in_stop_transition = TRUE;
04167 
04168    for (i = 0; i < MAX_CHANNELS; i++)
04169       if (log_chn[i].handle && log_chn[i].type == LOG_TYPE_DISK) {
04170          cm_msg(MINFO, "tr_start_abort", "Deleting previous file \"%s\"", log_chn[i].path);
04171          unlink(log_chn[i].path);
04172       }
04173 
04174    close_channels(run_number, NULL);
04175    close_buffers();
04176 
04177    in_stop_transition = FALSE;
04178 
04179    local_state = STATE_STOPPED;
04180 
04181    return CM_SUCCESS;
04182 }
04183 
04184 /*-- poststop ------------------------------------------------------*/
04185 
04186 INT tr_stop(INT run_number, char *error)
04187 /********************************************************************\
04188 
04189    Poststop:
04190 
04191      Wait until buffers are empty, then close logging channels
04192 
04193 \********************************************************************/
04194 {
04195    INT  size;
04196    BOOL flag, tape_flag = FALSE;
04197    char filename[256];
04198    char str[256];
04199 
04200    if (verbose)
04201       printf("tr_stop: run %d\n", run_number);
04202 
04203    if (in_stop_transition)
04204       return CM_SUCCESS;
04205 
04206    in_stop_transition = TRUE;
04207 
04208    close_channels(run_number, &tape_flag);
04209    close_buffers();
04210 
04211    /* ODB dump if requested */
04212    size = sizeof(flag);
04213    flag = 0;
04214    db_get_value(hDB, 0, "/Logger/ODB Dump", &flag, &size, TID_BOOL, TRUE);
04215    if (flag) {
04216       strcpy(str, "run%d.odb");
04217       size = sizeof(str);
04218       str[0] = 0;
04219       db_get_value(hDB, 0, "/Logger/ODB Dump File", str, &size, TID_STRING, TRUE);
04220       if (str[0] == 0)
04221          strcpy(str, "run%d.odb");
04222 
04223       /* substitue "%d" by current run number */
04224       if (strchr(str, '%'))
04225          sprintf(filename, str, run_number);
04226       else
04227          strcpy(filename, str);
04228 
04229       odb_save(filename);
04230    }
04231 #ifdef HAVE_MYSQL
04232    /* write to SQL database if requested */
04233    write_sql(FALSE);
04234 #endif
04235 
04236    in_stop_transition = FALSE;
04237 
04238    if (tape_flag & tape_message)
04239       cm_msg(MTALK, "tr_stop", "all tape channels closed");
04240 
04241    /* write transition event into history */
04242    write_history(STATE_STOPPED, run_number);
04243 
04244    /* clear flag */
04245    stop_requested = FALSE;
04246 
04247    if (start_requested) {
04248       int delay = 0;
04249       size = sizeof(delay);
04250       db_get_value(hDB, 0, "/Logger/Auto restart delay", &delay, &size, TID_INT, TRUE);
04251       auto_restart = ss_time() + delay; /* start after specified delay */
04252       start_requested = FALSE;
04253    }
04254 
04255    local_state = STATE_STOPPED;
04256 
04257    return CM_SUCCESS;
04258 }
04259 
04260 /*== common code FAL/MLOGGER end ===================================*/
04261 
04262 /*----- pause/resume -----------------------------------------------*/
04263 
04264 INT tr_pause(INT run_number, char *error)
04265 {
04266    /* write transition event into history */
04267    write_history(STATE_PAUSED, run_number);
04268 
04269    local_state = STATE_PAUSED;
04270 
04271    return CM_SUCCESS;
04272 }
04273 
04274 INT tr_resume(INT run_number, char *error)
04275 {
04276    /* write transition event into history */
04277    write_history(STATE_RUNNING, run_number);
04278 
04279    local_state = STATE_RUNNING;
04280 
04281    return CM_SUCCESS;
04282 }
04283 
04284 /*----- receive_event ----------------------------------------------*/
04285 
04286 void receive_event(HNDLE hBuf, HNDLE request_id, EVENT_HEADER * pheader, void *pevent)
04287 {
04288    INT i;
04289 
04290    if (verbose)
04291       printf("write data event: req %d, evid %d, timestamp %d, size %d\n", request_id, pheader->event_id, pheader->time_stamp, pheader->data_size);
04292 
04293    /* find logging channel for this request id */
04294    for (i = 0; i < MAX_CHANNELS; i++) {
04295       if (log_chn[i].handle == 0 && log_chn[i].ftp_con == NULL)
04296          continue;
04297 
04298       /* write normal events */
04299       if (log_chn[i].request_id == request_id) {
04300          log_write(&log_chn[i], pheader);
04301          break;
04302       }
04303 
04304       /* write messages */
04305       if (log_chn[i].msg_request_id == request_id) {
04306          log_write(&log_chn[i], pheader);
04307          break;
04308       }
04309    }
04310 }
04311 
04312 /*------------------------ main ------------------------------------*/
04313 
04314 int main(int argc, char *argv[])
04315 {
04316    INT status, msg, i, size, ch = 0;
04317    char host_name[HOST_NAME_LENGTH], exp_name[NAME_LENGTH], dir[256];
04318    BOOL debug, daemon, save_mode;
04319    DWORD last_time_kb = 0;
04320    DWORD last_time_stat = 0;
04321    DWORD duration;
04322    HNDLE hktemp;
04323 
04324 #ifdef HAVE_ROOT
04325    char **rargv;
04326    int rargc;
04327 
04328    /* copy first argument */
04329    rargc = 0;
04330    rargv = (char **) malloc(sizeof(char *) * 2);
04331    rargv[rargc] = (char *) malloc(strlen(argv[rargc]) + 1);
04332    strcpy(rargv[rargc], argv[rargc]);
04333    rargc++;
04334 
04335    /* append argument "-b" for batch mode without graphics */
04336    rargv[rargc++] = "-b";
04337 
04338    TApplication theApp("mlogger", &rargc, rargv);
04339 
04340    /* free argument memory */
04341    free(rargv[0]);
04342    free(rargv);
04343 
04344 #endif
04345 
04346    setbuf(stdout, NULL);
04347    setbuf(stderr, NULL);
04348 
04349    /* get default from environment */
04350    cm_get_environment(host_name, sizeof(host_name), exp_name, sizeof(exp_name));
04351 
04352    debug = daemon = save_mode = FALSE;
04353 
04354    /* parse command line parameters */
04355    for (i = 1; i < argc; i++) {
04356       if (argv[i][0] == '-' && argv[i][1] == 'd')
04357          debug = TRUE;
04358       else if (argv[i][0] == '-' && argv[i][1] == 'D')
04359          daemon = TRUE;
04360       else if (argv[i][0] == '-' && argv[i][1] == 's')
04361          save_mode = TRUE;
04362       else if (argv[i][0] == '-' && argv[i][1] == 'v')
04363          verbose = TRUE;
04364       else if (argv[i][0] == '-') {
04365          if (i + 1 >= argc || argv[i + 1][0] == '-')
04366             goto usage;
04367          if (argv[i][1] == 'e')
04368             strcpy(exp_name, argv[++i]);
04369          else {
04370           usage:
04371             printf("usage: mlogger [-e Experiment] [-d] [-D] [-s] [-v]\n\n");
04372             return 1;
04373          }
04374       }
04375    }
04376 
04377    if (daemon) {
04378       printf("Becoming a daemon...\n");
04379       ss_daemon_init(FALSE);
04380    }
04381 
04382    status = cm_connect_experiment(host_name, exp_name, "Logger", NULL);
04383    if (status != CM_SUCCESS)
04384       return 1;
04385 
04386    /* check if logger already running */
04387    status = cm_exist("Logger", FALSE);
04388    if (status == CM_SUCCESS) {
04389       printf("Logger runs already.\n");
04390       cm_disconnect_experiment();
04391       return 1;
04392    }
04393 
04394    cm_get_experiment_database(&hDB, NULL);
04395 
04396    /* set default watchdog timeout */
04397    cm_set_watchdog_params(TRUE, LOGGER_TIMEOUT);
04398 
04399    /* turn off watchdog if in debug mode */
04400    if (debug)
04401       cm_set_watchdog_params(TRUE, 0);
04402 
04403    /* turn on save mode */
04404    if (save_mode) {
04405       cm_set_watchdog_params(FALSE, 0);
04406       db_protect_database(hDB);
04407    }
04408 
04409    /* register transition callbacks */
04410    if (cm_register_transition(TR_START, tr_start, 200) != CM_SUCCESS) {
04411       cm_msg(MERROR, "main", "cannot register callbacks");
04412       return 1;
04413    }
04414 
04415    cm_register_transition(TR_STARTABORT, tr_start_abort, 800);
04416    cm_register_transition(TR_STOP, tr_stop, 800);
04417    cm_register_transition(TR_PAUSE, tr_pause, 800);
04418    cm_register_transition(TR_RESUME, tr_resume, 200);
04419 
04420    /* register callback for rewinding tapes */
04421    cm_register_function(RPC_LOG_REWIND, log_callback);
04422 
04423    /* initialize ODB */
04424    logger_init();
04425 
04426    /* obtain current state */
04427    local_state = STATE_STOPPED;
04428    size = sizeof(local_state);
04429    status = db_get_value(hDB, 0, "/Runinfo/State", &local_state, &size, TID_INT, true);
04430 
04431    /* open history logging */
04432    if (open_history() != CM_SUCCESS) {
04433       printf("Error in history system, aborting startup.\n");
04434       cm_disconnect_experiment();
04435       return 1;
04436    }
04437 
04438    /* turn off message display, turn on message logging */
04439    cm_set_msg_print(MT_ALL, 0, NULL);
04440 
04441    /* print startup message */
04442    size = sizeof(dir);
04443    db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
04444    printf("Log     directory is %s\n", dir);
04445    printf("Data    directory is same as Log unless specified in channels/\n");
04446 
04447    /* Alternate History and Elog path */
04448    size = sizeof(dir);
04449    dir[0] = 0;
04450    status = db_find_key(hDB, 0, "/Logger/History dir", &hktemp);
04451    if (status == DB_SUCCESS)
04452       db_get_value(hDB, 0, "/Logger/History dir", dir, &size, TID_STRING, TRUE);
04453    else
04454       sprintf(dir, "same as Log");
04455    printf("History directory is %s\n", dir);
04456 
04457    size = sizeof(dir);
04458    dir[0] = 0;
04459    status = db_find_key(hDB, 0, "/Logger/Elog dir", &hktemp);
04460    if (status == DB_SUCCESS)
04461       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, TRUE);
04462    else
04463       sprintf(dir, "same as Log");
04464    printf("ELog    directory is %s\n", dir);
04465 
04466 #ifdef HAVE_MYSQL
04467    {
04468       char sql_host[256], sql_db[256], sql_table[256];
04469 
04470       status = db_find_key(hDB, 0, "/Logger/SQL/Hostname", &hktemp);
04471       if (status == DB_SUCCESS) {
04472          size = 256;
04473          db_get_value(hDB, 0, "/Logger/SQL/Hostname", sql_host, &size, TID_STRING, FALSE);
04474          size = 256;
04475          db_get_value(hDB, 0, "/Logger/SQL/Database", sql_db, &size, TID_STRING, FALSE);
04476          size = 256;
04477          db_get_value(hDB, 0, "/Logger/SQL/Table", sql_table, &size, TID_STRING, FALSE);
04478          printf("SQL     database is %s/%s/%s", sql_host, sql_db, sql_table);
04479       }
04480    }
04481 #endif
04482 
04483    printf("\nMIDAS logger started. Stop with \"!\"\n");
04484 
04485    /* initialize ss_getchar() */
04486    ss_getchar(0);
04487 
04488    do {
04489       msg = cm_yield(1000);
04490 
04491       /* update channel statistics once every second */
04492       if (ss_millitime() - last_time_stat > 1000) {
04493          last_time_stat = ss_millitime();
04494          db_send_changed_records();
04495       }
04496 
04497       /* check for auto restart */
04498       if (auto_restart && ss_time() > auto_restart) {
04499          status = start_the_run();
04500       }
04501 
04502       /* check if time is reached to stop run */
04503       duration = 0;
04504       size = sizeof(duration);
04505       db_get_value(hDB, 0, "/Logger/Run duration", &duration, &size, TID_DWORD, true);
04506       if (!stop_requested && !in_stop_transition && local_state != STATE_STOPPED &&
04507           duration > 0 && ss_time() >= run_start_time + duration) {
04508          cm_msg(MTALK, "main", "stopping run after %d seconds", duration);
04509          status = stop_the_run(1);
04510       }
04511 
04512       /* check keyboard once every second */
04513       if (ss_millitime() - last_time_kb > 1000) {
04514          last_time_kb = ss_millitime();
04515 
04516          ch = 0;
04517          while (ss_kbhit()) {
04518             ch = ss_getchar(0);
04519             if (ch == -1)
04520                ch = getchar();
04521 
04522             if ((char) ch == '!')
04523                break;
04524          }
04525       }
04526 
04527    } while (msg != RPC_SHUTDOWN && msg != SS_ABORT && ch != '!');
04528 
04529    /* reset terminal */
04530    ss_getchar(TRUE);
04531 
04532    /* close history logging */
04533    close_history();
04534 
04535    cm_disconnect_experiment();
04536 
04537    return 0;
04538 }

Midas DOC Version 3.0.0 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Sergio Ballestrero - Suzannah Daviel - Doxygen - Peter Green - Qing Gu - Greg Hackman - Gertjan Hofman - Paul Knowles - Exaos Lee - Rudi Meier - Glenn Moloney - Dave Morris - John M O'Donnell - Konstantin Olchanski - Renee Poutissou - Tamsen Schurman - Andreas Suter - Jan M.Wouters - Piotr Adam Zolnierczuk