LCOV - code coverage report
Current view: top level - src - midas.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 17.3 % 7163 1238
Test Date: 2025-11-11 10:26:08 Functions: 30.7 % 300 92

            Line data    Source code
       1              : /********************************************************************\
       2              : 
       3              :   Name:         MIDAS.C
       4              :   Created by:   Stefan Ritt
       5              : 
       6              :   Contents:     MIDAS main library funcitons
       7              : 
       8              :   $Id$
       9              : 
      10              : \********************************************************************/
      11              : 
      12              : #undef NDEBUG // midas required assert() to be always enabled
      13              : 
      14              : #include "midas.h"
      15              : #include "msystem.h"
      16              : #include "git-revision.h"
      17              : #include "mstrlcpy.h"
      18              : #include "odbxx.h"
      19              : 
      20              : #include <assert.h>
      21              : #include <signal.h>
      22              : #include <sys/resource.h>
      23              : 
      24              : #include <mutex>
      25              : #include <deque>
      26              : #include <thread>
      27              : #include <atomic>
      28              : #include <algorithm>
      29              : 
      30              : /**dox***************************************************************/
      31              : /** @file midas.c
      32              : The main core C-code for Midas.
      33              : */
      34              : 
      35              : /**
      36              : \mainpage MIDAS code documentation
      37              : 
      38              : Welcome to the doxygen-generated documentation for the MIDAS source code.
      39              : 
      40              : This documentation is intended to be used as reference by MIDAS developers
      41              : and advanced users.
      42              : 
      43              : Documentation for new users, general information on MIDAS, examples,
      44              : user discussion, mailing lists and forums,
      45              : can be found through the MIDAS Wiki at http://midas.triumf.ca
      46              : */
      47              : 
      48              : /** @defgroup cmfunctionc Common Functions (cm_xxx)
      49              :  */
      50              : /** @defgroup bmfunctionc Event Buffer Functions (bm_xxx)
      51              :  */
      52              : /** @defgroup msgfunctionc Message Functions (msg_xxx)
      53              :  */
      54              : /** @defgroup bkfunctionc Data Bank Functions (bk_xxx)
      55              :  */
      56              : /** @defgroup rpc_xxx RPC Functions (rpc_xxx)
      57              :  */
      58              : /** @defgroup rbfunctionc Ring Buffer Functions (rb_xxx)
      59              :  */
      60              : 
      61              : /**dox***************************************************************/
      62              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
      63              : 
      64              : /********************************************************************/
      65              : /* data type sizes */
      66              : static const int tid_size[] = {
      67              :         0,   /* tid == 0 not defined                               */
      68              :         1,   /* TID_UINT8     unsigned byte         0       255    */
      69              :         1,   /* TID_INT8      signed byte         -128      127    */
      70              :         1,   /* TID_CHAR      single character      0       255    */
      71              :         2,   /* TID_UINT16    two bytes             0      65535   */
      72              :         2,   /* TID_INT16     signed word        -32768    32767   */
      73              :         4,   /* TID_UINT32    four bytes            0      2^32-1  */
      74              :         4,   /* TID_INT32     signed dword        -2^31    2^31-1  */
      75              :         4,   /* TID_BOOL      four bytes bool       0        1     */
      76              :         4,   /* TID_FLOAT     4 Byte float format                  */
      77              :         8,   /* TID_DOUBLE    8 Byte float format                  */
      78              :         4,   /* TID_BITFIELD  32 Bits Bitfield    0000... 11111... */
      79              :         0,   /* TID_STRING    zero terminated string               */
      80              :         0,   /* TID_ARRAY     variable length array of unkown type */
      81              :         0,   /* TID_STRUCT    C structure                          */
      82              :         0,   /* TID_KEY       key in online database               */
      83              :         0,   /* TID_LINK      link in online database              */
      84              :         8,   /* TID_INT64     8 bytes int          -2^63   2^63-1  */
      85              :         8    /* TID_UINT64    8 bytes unsigned int  0      2^64-1  */
      86              : };
      87              : 
      88              : /* data type names */
      89              : static const char *tid_name_old[] = {
      90              :         "NULL",
      91              :         "BYTE",
      92              :         "SBYTE",
      93              :         "CHAR",
      94              :         "WORD",
      95              :         "SHORT",
      96              :         "DWORD",
      97              :         "INT",
      98              :         "BOOL",
      99              :         "FLOAT",
     100              :         "DOUBLE",
     101              :         "BITFIELD",
     102              :         "STRING",
     103              :         "ARRAY",
     104              :         "STRUCT",
     105              :         "KEY",
     106              :         "LINK",
     107              :         "INT64",
     108              :         "UINT64"
     109              : };
     110              : 
     111              : static const char *tid_name[] = {
     112              :         "NULL",
     113              :         "UINT8",
     114              :         "INT8",
     115              :         "CHAR",
     116              :         "UINT16",
     117              :         "INT16",
     118              :         "UINT32",
     119              :         "INT32",
     120              :         "BOOL",
     121              :         "FLOAT",
     122              :         "DOUBLE",
     123              :         "BITFIELD",
     124              :         "STRING",
     125              :         "ARRAY",
     126              :         "STRUCT",
     127              :         "KEY",
     128              :         "LINK",
     129              :         "INT64",
     130              :         "UINT64"
     131              : };
     132              : 
     133            5 : std::string cm_transition_name(int transition)
     134              : {
     135            7 :    if (transition == TR_START) return "START";
     136            6 :    if (transition == TR_STOP)  return "STOP";
     137            5 :    if (transition == TR_PAUSE) return "PAUSE";
     138            4 :    if (transition == TR_RESUME) return "RESUME";
     139            3 :    if (transition == TR_STARTABORT) return "STARTABORT";
     140            0 :    if (transition == TR_DEFERRED) return "DEFERRED";
     141            0 :    return msprintf("UNKNOWN TRANSITION %d", transition);
     142              : }
     143              : 
     144              : const char *mname[] = {
     145              :         "January",
     146              :         "February",
     147              :         "March",
     148              :         "April",
     149              :         "May",
     150              :         "June",
     151              :         "July",
     152              :         "August",
     153              :         "September",
     154              :         "October",
     155              :         "November",
     156              :         "December"
     157              : };
     158              : 
     159              : /* Globals */
     160              : #ifdef OS_MSDOS
     161              : extern unsigned _stklen = 60000U;
     162              : #endif
     163              : 
     164              : extern DATABASE *_database;
     165              : extern INT _database_entries;
     166              : 
     167              : //
     168              : // locking rules for gBuffers and gBuffersMutex:
     169              : //
     170              : // - all access to gBuffers must be done while holding gBufferMutex
     171              : // - while holding gBufferMutex:
     172              : // - taking additional locks not permitted (no calling odb, no locking event buffers, etc)
     173              : // - calling functions that can take additional locks not permitted (no calling db_xxx(), bm_xxx(), etc)
     174              : // - calling functions that can come back recursively not permitted
     175              : //
     176              : // after obtaining a BUFFER*pbuf pointer from gBuffers:
     177              : //
     178              : // - holding gBuffersMutex is not required
     179              : // - to access pbuf data, must hold buffer_mutex or call bm_lock_buffer()
     180              : // - except for:
     181              : //     pbuf->attached - no need to hold a lock (std::atomic)
     182              : //     pbuf->buffer_name - no need to hold a lock (constant data, only changed by bm_open_buffer())
     183              : //
     184              : // object life time:
     185              : //
     186              : // - gBuffers never shrinks
     187              : // - new BUFFER objects are created by bm_open_buffer(), added to gBuffers when ready for use, pbuf->attached set to true
     188              : // - bm_close_buffer() sets pbuf->attached to false
     189              : // - BUFFER objects are never deleted to avoid race between delete and bm_send_event() & co
     190              : // - BUFFER objects are never reused, bm_open_buffer() always creates a new object
     191              : // - gBuffers[i] set to NULL are empty slots available for reuse
     192              : // - closed buffers have corresponding gBuffers[i]->attached set to false
     193              : // 
     194              : 
     195              : static std::mutex gBuffersMutex; // protects gBuffers vector itself, but not it's contents!
     196              : static std::vector<BUFFER*> gBuffers;
     197              : 
     198              : static INT _msg_buffer = 0;
     199              : static EVENT_HANDLER *_msg_dispatch = NULL;
     200              : 
     201              : /* Event request descriptor */
     202              : 
     203              : struct EventRequest
     204              : {
     205              :    INT buffer_handle = 0;            /* Buffer handle */
     206              :    short int event_id = 0;           /* same as in EVENT_HEADER */
     207              :    short int trigger_mask = 0;       /* same as in EVENT_HEADER */
     208              :    EVENT_HANDLER* dispatcher = NULL; /* Dispatcher func. */
     209              : 
     210            0 :    void clear()
     211              :    {
     212            0 :       buffer_handle = 0;
     213            0 :       event_id = 0;
     214            0 :       trigger_mask = 0;
     215            0 :       dispatcher = NULL;
     216            0 :    }
     217              : };
     218              : 
     219              : static std::mutex _request_list_mutex;
     220              : static std::vector<EventRequest> _request_list;
     221              : 
     222              : //static char *_tcp_buffer = NULL;
     223              : //static INT _tcp_wp = 0;
     224              : //static INT _tcp_rp = 0;
     225              : //static INT _tcp_sock = 0;
     226              : 
     227              : static MUTEX_T *_mutex_rpc = NULL; // mutex to protect RPC calls
     228              : 
     229              : static void (*_debug_print)(const char *) = NULL;
     230              : 
     231              : static INT _debug_mode = 0;
     232              : 
     233              : static int _rpc_connect_timeout = 10000;
     234              : 
     235              : // for use on a single machine it is best to restrict RPC access to localhost
     236              : // by binding the RPC listener socket to the localhost IP address.
     237              : static int disable_bind_rpc_to_localhost = 0;
     238              : 
     239              : /* table for transition functions */
     240              : 
     241              : struct TRANS_TABLE {
     242              :    INT transition;
     243              :    INT sequence_number;
     244              :    INT (*func)(INT, char *);
     245              : };
     246              : 
     247              : static std::mutex _trans_table_mutex;
     248              : static std::vector<TRANS_TABLE> _trans_table;
     249              : 
     250              : static TRANS_TABLE _deferred_trans_table[] = {
     251              :         {TR_START,  0, NULL},
     252              :         {TR_STOP,   0, NULL},
     253              :         {TR_PAUSE,  0, NULL},
     254              :         {TR_RESUME, 0, NULL},
     255              :         {0,         0, NULL}
     256              : };
     257              : 
     258              : static BOOL _rpc_registered = FALSE;
     259              : static int _rpc_listen_socket = 0;
     260              : 
     261              : static INT rpc_transition_dispatch(INT idx, void *prpc_param[]);
     262              : 
     263              : void cm_ctrlc_handler(int sig);
     264              : 
     265              : typedef struct {
     266              :    INT code;
     267              :    const char *string;
     268              : } ERROR_TABLE;
     269              : 
     270              : static const ERROR_TABLE _error_table[] = {
     271              :         {CM_WRONG_PASSWORD, "Wrong password"},
     272              :         {CM_UNDEF_EXP,      "Experiment not defined"},
     273              :         {CM_UNDEF_ENVIRON,
     274              :                             "\"exptab\" file not found and MIDAS_DIR or MIDAS_EXPTAB environment variable is not defined"},
     275              :         {RPC_NET_ERROR,     "Cannot connect to remote host"},
     276              :         {0, NULL}
     277              : };
     278              : 
     279              : typedef struct {
     280              :    void *adr;
     281              :    int size;
     282              :    char file[80];
     283              :    int line;
     284              : } DBG_MEM_LOC;
     285              : 
     286              : static DBG_MEM_LOC *_mem_loc = NULL;
     287              : static INT _n_mem = 0;
     288              : 
     289              : struct TR_PARAM
     290              : {
     291              :    INT transition;
     292              :    INT run_number;
     293              :    char *errstr;
     294              :    INT errstr_size;
     295              :    INT async_flag;
     296              :    INT debug_flag;
     297              :    std::atomic_int status{0};
     298              :    std::atomic_bool finished{false};
     299              :    std::atomic<std::thread*> thread{NULL};
     300              : };
     301              : 
     302              : static TR_PARAM _trp;
     303              : 
     304              : /*------------------------------------------------------------------*/
     305              : 
     306            0 : void *dbg_malloc(unsigned int size, char *file, int line) {
     307              :    FILE *f;
     308              :    void *adr;
     309              :    int i;
     310              : 
     311            0 :    adr = malloc(size);
     312              : 
     313              :    /* search for deleted entry */
     314            0 :    for (i = 0; i < _n_mem; i++)
     315            0 :       if (_mem_loc[i].adr == NULL)
     316            0 :          break;
     317              : 
     318            0 :    if (i == _n_mem) {
     319            0 :       _n_mem++;
     320            0 :       if (!_mem_loc)
     321            0 :          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
     322              :       else
     323            0 :          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
     324              :    }
     325              : 
     326            0 :    assert(_mem_loc != NULL);
     327              : 
     328            0 :    _mem_loc[i].adr = adr;
     329            0 :    _mem_loc[i].size = size;
     330            0 :    strcpy(_mem_loc[i].file, file);
     331            0 :    _mem_loc[i].line = line;
     332              : 
     333            0 :    f = fopen("mem.txt", "w");
     334              :    
     335            0 :    assert(f != NULL);
     336              : 
     337            0 :    for (i = 0; i < _n_mem; i++)
     338            0 :       if (_mem_loc[i].adr)
     339            0 :          fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line, _mem_loc[i].size, _mem_loc[i].adr);
     340              : 
     341            0 :    fclose(f);
     342              : 
     343            0 :    return adr;
     344              : }
     345              : 
     346            0 : void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line) {
     347              :    void *adr;
     348              : 
     349            0 :    adr = dbg_malloc(size * count, file, line);
     350            0 :    if (adr)
     351            0 :       memset(adr, 0, size * count);
     352              : 
     353            0 :    return adr;
     354              : }
     355              : 
     356            0 : void dbg_free(void *adr, char *file, int line) {
     357              :    FILE *f;
     358              :    int i;
     359              : 
     360            0 :    free(adr);
     361              : 
     362            0 :    for (i = 0; i < _n_mem; i++)
     363            0 :       if (_mem_loc[i].adr == adr)
     364            0 :          break;
     365              : 
     366            0 :    if (i < _n_mem)
     367            0 :       _mem_loc[i].adr = NULL;
     368              : 
     369            0 :    f = fopen("mem.txt", "w");
     370              :    
     371            0 :    assert(f != NULL);
     372              : 
     373            0 :    for (i = 0; i < _n_mem; i++)
     374            0 :       if (_mem_loc[i].adr)
     375            0 :          fprintf(f, "%s:%d %s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
     376            0 :                  file, line, _mem_loc[i].size, _mem_loc[i].adr);
     377              : 
     378            0 :    fclose(f);
     379            0 : }
     380              : 
     381            0 : static std::vector<std::string> split(const char* sep, const std::string& s)
     382              : {
     383            0 :    unsigned sep_len = strlen(sep);
     384            0 :    std::vector<std::string> v;
     385            0 :    std::string::size_type pos = 0;
     386              :    while (1) {
     387            0 :       std::string::size_type next = s.find(sep, pos);
     388            0 :       if (next == std::string::npos) {
     389            0 :          v.push_back(s.substr(pos));
     390            0 :          break;
     391              :       }
     392            0 :       v.push_back(s.substr(pos, next-pos));
     393            0 :       pos = next+sep_len;
     394            0 :    }
     395            0 :    return v;
     396            0 : }
     397              : 
     398            0 : static std::string join(const char* sep, const std::vector<std::string>& v)
     399              : {
     400            0 :    std::string s;
     401              : 
     402            0 :    for (unsigned i=0; i<v.size(); i++) {
     403            0 :       if (i>0) {
     404            0 :          s += sep;
     405              :       }
     406            0 :       s += v[i];
     407              :    }
     408              : 
     409            0 :    return s;
     410            0 : }
     411              : 
     412            2 : bool ends_with_char(const std::string& s, char c)
     413              : {
     414            2 :    if (s.length() < 1)
     415            0 :       return false;
     416            2 :    return s[s.length()-1] == c;
     417              : }
     418              : 
     419           26 : std::string msprintf(const char *format, ...) {
     420              :    va_list ap, ap1;
     421           26 :    va_start(ap, format);
     422           26 :    va_copy(ap1, ap);
     423           26 :    size_t size = vsnprintf(nullptr, 0, format, ap1) + 1;
     424           26 :    char *buffer = (char *)malloc(size);
     425           26 :    if (!buffer) {
     426            0 :       va_end(ap);
     427            0 :       va_end(ap1);
     428            0 :       return "";
     429              :    }
     430           26 :    vsnprintf(buffer, size, format, ap);
     431           26 :    va_end(ap);
     432           26 :    va_end(ap1);
     433           26 :    std::string s(buffer);
     434           26 :    free(buffer);
     435           26 :    return s;
     436           26 : }
     437              : 
     438              : /********************************************************************\
     439              : *                                                                    *
     440              : *              Common message functions                              *
     441              : *                                                                    *
     442              : \********************************************************************/
     443              : 
     444              : typedef int (*MessagePrintCallback)(const char *);
     445              : 
     446              : static std::atomic<MessagePrintCallback> _message_print{puts};
     447              : 
     448              : static std::atomic_int _message_mask_system{MT_ALL};
     449              : static std::atomic_int _message_mask_user{MT_ALL};
     450              : 
     451              : 
     452              : /**dox***************************************************************/
     453              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
     454              : 
     455              : /**dox***************************************************************/
     456              : /** @addtogroup msgfunctionc
     457              :  *
     458              :  *  @{  */
     459              : 
     460              : /********************************************************************/
     461              : /**
     462              : Convert error code to string. Used after cm_connect_experiment to print
     463              : error string in command line programs or windows programs.
     464              : @param code Error code as defined in midas.h
     465              : @param string Error string
     466              : @return CM_SUCCESS
     467              : */
     468            0 : std::string cm_get_error(INT code)
     469              : {
     470            0 :    for (int i = 0; _error_table[i].code; i++) {
     471            0 :       if (_error_table[i].code == code) {
     472            0 :          return _error_table[i].string;
     473              :       }
     474              :    }
     475              : 
     476            0 :    return msprintf("unlisted status code %d", code);
     477              : }
     478              : 
     479              : /********************************************************************/
     480            2 : int cm_msg_early_init(void) {
     481              : 
     482            2 :    return CM_SUCCESS;
     483              : }
     484              : 
     485              : /********************************************************************/
     486              : 
     487            2 : int cm_msg_open_buffer(void) {
     488              :    //printf("cm_msg_open_buffer!\n");
     489            2 :    if (_msg_buffer == 0) {
     490            2 :       int status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
     491            2 :       if (status != BM_SUCCESS && status != BM_CREATED) {
     492            0 :          return status;
     493              :       }
     494              :    }
     495            2 :    return CM_SUCCESS;
     496              : }
     497              : 
     498              : /********************************************************************/
     499              : 
     500            4 : int cm_msg_close_buffer(void) {
     501              :    //printf("cm_msg_close_buffer!\n");
     502            4 :    if (_msg_buffer) {
     503            2 :       bm_close_buffer(_msg_buffer);
     504            2 :       _msg_buffer = 0;
     505              :    }
     506            4 :    return CM_SUCCESS;
     507              : }
     508              : 
     509              : /********************************************************************/
     510              : 
     511              : /**
     512              :  Retrieve list of message facilities by searching logfiles on disk
     513              :  @param  list             List of facilities
     514              :  @return status           SUCCESS
     515              :  */
     516              : 
     517            0 : INT EXPRT cm_msg_facilities(STRING_LIST *list) {
     518            0 :    std::string path;
     519              : 
     520            0 :    cm_msg_get_logfile("midas", 0, &path, NULL, NULL);
     521              : 
     522              :    /* extract directory name from full path name of midas.log */
     523            0 :    size_t pos = path.rfind(DIR_SEPARATOR);
     524            0 :    if (pos != std::string::npos) {
     525            0 :       path.resize(pos);
     526              :    } else {
     527            0 :       path = "";
     528              :    }
     529              : 
     530              :    //printf("cm_msg_facilities: path [%s]\n", path.c_str());
     531              : 
     532            0 :    STRING_LIST flist;
     533              :    
     534            0 :    ss_file_find(path.c_str(), "*.log", &flist);
     535              : 
     536            0 :    for (size_t i = 0; i < flist.size(); i++) {
     537            0 :       const char *p = flist[i].c_str();
     538            0 :       if (strchr(p, '_') == NULL && !(p[0] >= '0' && p[0] <= '9')) {
     539            0 :          size_t pos = flist[i].rfind('.');
     540            0 :          if (pos != std::string::npos) {
     541            0 :             flist[i].resize(pos);
     542              :          }
     543            0 :          list->push_back(flist[i]);
     544              :       }
     545              :    }
     546              : 
     547            0 :    return SUCCESS;
     548            0 : }
     549              : 
     550              : /********************************************************************/
     551              : 
     552           15 : void cm_msg_get_logfile(const char *fac, time_t t, std::string* filename, std::string* linkname, std::string* linktarget) {
     553              :    HNDLE hDB;
     554              :    int status;
     555              : 
     556           15 :    status = cm_get_experiment_database(&hDB, NULL);
     557              : 
     558              :    // check for call to cm_msg() before MIDAS is fully initialized
     559              :    // or after MIDAS is partially shutdown.
     560           15 :    if (status != CM_SUCCESS) {
     561            0 :       if (filename)
     562            0 :          *filename = std::string(fac) + ".log";
     563            0 :       if (linkname)
     564            0 :          *linkname = "";
     565            0 :       if (linktarget)
     566            0 :          *linktarget = "";
     567            0 :       return;
     568              :    }
     569              : 
     570           15 :    if (filename)
     571           15 :       *filename = "";
     572           15 :    if (linkname)
     573           15 :       *linkname = "";
     574           15 :    if (linktarget)
     575           15 :       *linktarget = "";
     576              : 
     577           15 :    std::string facility;
     578           15 :    if (fac && fac[0])
     579           15 :       facility = fac;
     580              :    else
     581            0 :       facility = "midas";
     582              : 
     583           15 :    std::string message_format;
     584           15 :    db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &message_format, TRUE);
     585           15 :    if (message_format.find('%') != std::string::npos) {
     586              :       /* replace stings such as %y%m%d with current date */
     587              :       struct tm tms;
     588              : 
     589            0 :       ss_tzset();
     590            0 :       if (t == 0)
     591            0 :          time(&t);
     592            0 :       localtime_r(&t, &tms);
     593              : 
     594              :       char de[256];
     595            0 :       de[0] = '_';
     596            0 :       strftime(de + 1, sizeof(de)-1, strchr(message_format.c_str(), '%'), &tms);
     597            0 :       message_format = de;
     598              :    }
     599              : 
     600           15 :    std::string message_dir;
     601           15 :    db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
     602           15 :    if (message_dir.empty()) {
     603           15 :       db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &message_dir, FALSE);
     604           15 :       if (message_dir.empty()) {
     605            0 :          message_dir = cm_get_path();
     606            0 :          if (message_dir.empty()) {
     607            0 :             message_dir = ss_getcwd();
     608              :          }
     609              :       }
     610              :    }
     611              : 
     612              :    // prepend experiment directory
     613           15 :    if (message_dir[0] != DIR_SEPARATOR)
     614            0 :       message_dir = cm_get_path() + message_dir;
     615              : 
     616           15 :    if (message_dir.back() != DIR_SEPARATOR)
     617            0 :       message_dir.push_back(DIR_SEPARATOR);
     618              : 
     619           15 :    if (filename)
     620           15 :       *filename = message_dir + facility + message_format + ".log";
     621           15 :    if (!message_format.empty()) {
     622            0 :       if (linkname)
     623            0 :          *linkname = message_dir + facility  + ".log";
     624            0 :       if (linktarget)
     625            0 :          *linktarget = facility  + message_format + ".log";
     626              :    }
     627           15 : }
     628              : 
     629              : /********************************************************************/
     630              : /**
     631              : Set message masks. When a message is generated by calling cm_msg(),
     632              : it can got to two destinatinons. First a user defined callback routine
     633              : and second to the "SYSMSG" buffer.
     634              : 
     635              : A user defined callback receives all messages which satisfy the user_mask.
     636              : 
     637              : \code
     638              : int message_print(const char *msg)
     639              : {
     640              :   char str[160];
     641              : 
     642              :   memset(str, ' ', 159);
     643              :   str[159] = 0;
     644              :   if (msg[0] == '[')
     645              :     msg = strchr(msg, ']')+2;
     646              :   memcpy(str, msg, strlen(msg));
     647              :   ss_printf(0, 20, str);
     648              :   return 0;
     649              : }
     650              : ...
     651              :   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
     652              : ...
     653              : \endcode
     654              : @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
     655              : @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
     656              : @param func Function which receives all printout. By setting "puts",
     657              :        messages are just printed to the screen.
     658              : @return CM_SUCCESS
     659              : */
     660            2 : INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func)(const char *)) {
     661            2 :    _message_mask_system = system_mask;
     662            2 :    _message_mask_user = user_mask;
     663            2 :    _message_print = func;
     664              : 
     665            2 :    return BM_SUCCESS;
     666              : }
     667              : 
     668              : /********************************************************************/
     669              : /**
     670              : Write message to logging file. Called by cm_msg.
     671              : @attention May burn your fingers
     672              : @param message_type     Message type
     673              : @param message          Message string
     674              : @param facility         Message facility, filename in which messages will be written
     675              : @return CM_SUCCESS
     676              : */
     677           15 : INT cm_msg_log(INT message_type, const char *facility, const char *message) {
     678              :    INT status;
     679              : 
     680           15 :    if (rpc_is_remote()) {
     681            0 :       if (rpc_is_connected()) {
     682            0 :          status = rpc_call(RPC_CM_MSG_LOG, message_type, facility, message);
     683            0 :          if (status != RPC_SUCCESS) {
     684            0 :             fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log because rpc_call(RPC_CM_MSG_LOG) failed with status %d\n", message, status);
     685              :          }
     686            0 :          return status;
     687              :       } else {
     688            0 :          fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log, no connection to mserver\n", message);
     689            0 :          return RPC_NET_ERROR;
     690              :       }
     691              :    }
     692              : 
     693           15 :    if (message_type != MT_DEBUG) {
     694           15 :       std::string filename, linkname, linktarget;
     695              : 
     696           15 :       cm_msg_get_logfile(facility, 0, &filename, &linkname, &linktarget);
     697              : 
     698              : #ifdef OS_LINUX
     699           15 :       if (!linkname.empty()) {
     700              :          //printf("cm_msg_log: filename [%s] linkname [%s] linktarget [%s]\n", filename.c_str(), linkname.c_str(), linktarget.c_str());
     701              :          // If filename does not exist, user just switched from non-date format to date format.
     702              :          // In that case we must copy linkname to filename, otherwise messages might get lost.
     703            0 :          if (ss_file_exist(linkname.c_str()) && !ss_file_link_exist(linkname.c_str())) {
     704            0 :             ss_file_copy(linkname.c_str(), filename.c_str(), true);
     705              :          }
     706              : 
     707            0 :          unlink(linkname.c_str());
     708            0 :          status = symlink(linktarget.c_str(), linkname.c_str());
     709            0 :          if (status != 0) {
     710            0 :             fprintf(stderr,
     711              :                     "cm_msg_log: Error: Cannot symlink message log file \'%s' to \'%s\', symlink() errno: %d (%s)\n",
     712            0 :                     linktarget.c_str(), linkname.c_str(), errno, strerror(errno));
     713              :          }
     714              :       }
     715              : #endif
     716              : 
     717           15 :       int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
     718           15 :       if (fh < 0) {
     719            0 :          fprintf(stderr,
     720              :                  "cm_msg_log: Message \"%s\" not written to midas.log because open(%s) failed with errno %d (%s)\n",
     721            0 :                  message, filename.c_str(), errno, strerror(errno));
     722              :       } else {
     723              : 
     724              :          struct timeval tv;
     725              :          struct tm tms;
     726              : 
     727           15 :          ss_tzset();
     728           15 :          gettimeofday(&tv, NULL);
     729           15 :          localtime_r(&tv.tv_sec, &tms);
     730              : 
     731              :          char str[256];
     732           15 :          strftime(str, sizeof(str), "%H:%M:%S", &tms);
     733           15 :          sprintf(str + strlen(str), ".%03d ", (int) (tv.tv_usec / 1000));
     734           15 :          strftime(str + strlen(str), sizeof(str), "%G/%m/%d", &tms);
     735              : 
     736           15 :          std::string msg;
     737           15 :          msg += str;
     738           15 :          msg += " ";
     739           15 :          msg += message;
     740           15 :          msg += "\n";
     741              : 
     742              :          /* avoid c++ complaint about comparison between
     743              :             unsigned size_t returned by msg.length() and
     744              :             signed ssize_t returned by write() */
     745           15 :          ssize_t len = msg.length();
     746              : 
     747              :          /* atomic write, no need to take a semaphore */
     748           15 :          ssize_t wr = write(fh, msg.c_str(), len);
     749              : 
     750           15 :          if (wr < 0) {
     751            0 :             fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", write() error, errno %d (%s)\n", message, filename.c_str(), errno, strerror(errno));
     752           15 :          } else if (wr != len) {
     753            0 :             fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", short write() wrote %d instead of %d bytes\n", message, filename.c_str(), (int)wr, (int)len);
     754              :          }
     755              : 
     756           15 :          close(fh);
     757           15 :       }
     758           15 :    }
     759              : 
     760           15 :    return CM_SUCCESS;
     761              : }
     762              : 
     763              : 
     764           15 : static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
     765              : {
     766              :    /* strip path */
     767           15 :    const char* pc = filename + strlen(filename);
     768          143 :    while (*pc != '\\' && *pc != '/' && pc != filename)
     769          128 :       pc--;
     770           15 :    if (pc != filename)
     771           15 :       pc++;
     772              : 
     773              :    /* convert type to string */
     774           15 :    std::string type_str;
     775           15 :    if (message_type & MT_ERROR)
     776           11 :       type_str += MT_ERROR_STR;
     777           15 :    if (message_type & MT_INFO)
     778            0 :       type_str += MT_INFO_STR;
     779           15 :    if (message_type & MT_DEBUG)
     780            0 :       type_str += MT_DEBUG_STR;
     781           15 :    if (message_type & MT_USER)
     782            0 :       type_str += MT_USER_STR;
     783           15 :    if (message_type & MT_LOG)
     784            4 :       type_str += MT_LOG_STR;
     785           15 :    if (message_type & MT_TALK)
     786            0 :       type_str += MT_TALK_STR;
     787              : 
     788           15 :    std::string message;
     789              : 
     790              :    /* print client name into string */
     791           15 :    if (message_type == MT_USER)
     792            0 :       message = msprintf("[%s] ", routine);
     793              :    else {
     794           15 :       std::string name = rpc_get_name();
     795           15 :       if (name.length() > 0)
     796           15 :          message = msprintf("[%s,%s] ", name.c_str(), type_str.c_str());
     797              :       else
     798            0 :          message = "";
     799           15 :    }
     800              : 
     801              :    /* preceed error messages with file and line info */
     802           15 :    if (message_type == MT_ERROR) {
     803           11 :       message += msprintf("[%s:%d:%s,%s] ", pc, line, routine, type_str.c_str());
     804            4 :    } else if (message_type == MT_USER) {
     805            0 :       message = msprintf("[%s,%s] ", routine, type_str.c_str());
     806              :    }
     807              : 
     808           15 :    int bufsize = 1024;
     809           15 :    char* buf = (char*)malloc(bufsize);
     810           15 :    assert(buf);
     811              : 
     812           15 :    for (int i=0; i<10; i++) {
     813              :       va_list ap;
     814           15 :       va_copy(ap, *argptr);
     815              :       
     816              :       /* print argument list into message */
     817           15 :       int n = vsnprintf(buf, bufsize-1, format, ap);
     818              : 
     819              :       //printf("vsnprintf [%s] %d %d\n", format, bufsize, n);
     820              : 
     821           15 :       va_end(ap);
     822              : 
     823           15 :       if (n < bufsize) {
     824           15 :          break;
     825              :       }
     826              : 
     827            0 :       bufsize += 100;
     828            0 :       bufsize *= 2;
     829            0 :       buf = (char*)realloc(buf, bufsize);
     830            0 :       assert(buf);
     831              :    }
     832              : 
     833           15 :    message += buf;
     834           15 :    free(buf);
     835              : 
     836           30 :    return message;
     837           15 : }
     838              : 
     839           15 : static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message) {
     840              :    //printf("cm_msg_send: ts %d, type %d, message [%s]\n", ts, message_type, send_message);
     841              : 
     842              :    /* send event if not of type MLOG */
     843           15 :    if (message_type != MT_LOG) {
     844           11 :       if (_msg_buffer) {
     845              :          /* copy message to event */
     846           11 :          size_t len = strlen(send_message);
     847           11 :          int event_length = sizeof(EVENT_HEADER) + len + 1;
     848           11 :          char event[event_length];
     849           11 :          EVENT_HEADER *pevent = (EVENT_HEADER *) event;
     850              : 
     851           11 :          memcpy(event + sizeof(EVENT_HEADER), send_message, len + 1);
     852              : 
     853              :          /* setup the event header and send the message */
     854           11 :          bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, len + 1, 0);
     855           11 :          if (ts)
     856           11 :             pevent->time_stamp = ts;
     857              :          //printf("cm_msg_send_event: len %d, header %d, allocated %d, data_size %d, bm_send_event %p+%d\n", (int)len, (int)sizeof(EVENT_HEADER), event_length, pevent->data_size, pevent, (int)(pevent->data_size + sizeof(EVENT_HEADER)));
     858           11 :          bm_send_event(_msg_buffer, pevent, 0, BM_WAIT);
     859           11 :       }
     860              :    }
     861              : 
     862           15 :    return CM_SUCCESS;
     863              : }
     864              : 
     865              : struct msg_buffer_entry {
     866              :    DWORD ts;
     867              :    int message_type;
     868              :    std::string message;
     869              : };
     870              : 
     871              : static std::deque<msg_buffer_entry> gMsgBuf;
     872              : static std::mutex gMsgBufMutex;
     873              : 
     874              : /********************************************************************/
     875              : /**
     876              : This routine can be called to process messages buffered by cm_msg(). Normally
     877              : it is called from cm_yield() and cm_disconnect_experiment() to make sure
     878              : all accumulated messages are processed.
     879              : */
     880            5 : INT cm_msg_flush_buffer() {
     881              :    int i;
     882              : 
     883              :    //printf("cm_msg_flush_buffer!\n");
     884              : 
     885           20 :    for (i = 0; i < 100; i++) {
     886           20 :       msg_buffer_entry e;
     887              :       {
     888           20 :          std::lock_guard<std::mutex> lock(gMsgBufMutex);
     889           20 :          if (gMsgBuf.empty())
     890            5 :             break;
     891           15 :          e = gMsgBuf.front();
     892           15 :          gMsgBuf.pop_front();
     893              :          // implicit unlock
     894           20 :       }
     895              : 
     896              :       /* log message */
     897           15 :       cm_msg_log(e.message_type, "midas", e.message.c_str());
     898              : 
     899              :       /* send message to SYSMSG */
     900           15 :       int status = cm_msg_send_event(e.ts, e.message_type, e.message.c_str());
     901           15 :       if (status != CM_SUCCESS)
     902            0 :          return status;
     903           20 :    }
     904              : 
     905            5 :    return CM_SUCCESS;
     906              : }
     907              : 
     908              : /********************************************************************/
     909              : /**
     910              : This routine can be called whenever an internal error occurs
     911              : or an informative message is produced. Different message
     912              : types can be enabled or disabled by setting the type bits
     913              : via cm_set_msg_print().
     914              : @attention Do not add the "\n" escape carriage control at the end of the
     915              : formated line as it is already added by the client on the receiving side.
     916              : \code
     917              :    ...
     918              :    cm_msg(MINFO, "my program", "This is a information message only);
     919              :    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
     920              :    cm_msg(MTALK, "my_program", My program is Done!");
     921              :    ...
     922              : \endcode
     923              : @param message_type (See @ref midas_macro).
     924              : @param filename Name of source file where error occured
     925              : @param line Line number where error occured
     926              : @param routine Routine name.
     927              : @param format message to printout, ... Parameters like for printf()
     928              : @return CM_SUCCESS
     929              : */
     930           15 : INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
     931              : {
     932           15 :    DWORD ts = ss_time();
     933              : 
     934              :    /* print argument list into message */
     935           15 :    std::string message;
     936              :    va_list argptr;
     937           15 :    va_start(argptr, format);
     938           15 :    message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
     939           15 :    va_end(argptr);
     940              : 
     941              :    //printf("message [%s]\n", message.c_str());
     942              : 
     943              :    /* call user function if set via cm_set_msg_print */
     944           15 :    MessagePrintCallback f = _message_print.load();
     945           15 :    if (f != NULL && (message_type & _message_mask_user) != 0) {
     946           15 :       if (message_type != MT_LOG) { // do not print MLOG messages
     947           11 :          (*f)(message.c_str());
     948              :       }
     949              :    }
     950              : 
     951              :    /* return if system mask is not set */
     952           15 :    if ((message_type & _message_mask_system) == 0) {
     953            0 :       return CM_SUCCESS;
     954              :    }
     955              : 
     956           15 :    gMsgBufMutex.lock();
     957           15 :    gMsgBuf.push_back(msg_buffer_entry{ts, message_type, message});
     958           15 :    gMsgBufMutex.unlock();
     959              : 
     960           15 :    return CM_SUCCESS;
     961           15 : }
     962              : 
     963              : /********************************************************************/
     964              : /**
     965              : This routine is similar to @ref cm_msg().
     966              : It differs from cm_msg() only by the logging destination being a file
     967              : given through the argument list i.e:\b facility
     968              : @internal
     969              : @attention Do not add the "\n" escape carriage control at the end of the
     970              : formated line as it is already added by the client on the receiving side.
     971              : The first arg in the following example uses the predefined
     972              : macro MINFO which handles automatically the first 3 arguments of the function
     973              : (see @ref midas_macro).
     974              : \code   ...
     975              :    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
     976              :    ...
     977              : //----- File my_log_file.log
     978              : Thu Nov  8 17:59:28 2001 [my_program] My message status:1
     979              : \endcode
     980              : @param message_type See @ref midas_macro.
     981              : @param filename Name of source file where error occured
     982              : @param line Line number where error occured
     983              : @param facility Logging file name
     984              : @param routine Routine name
     985              : @param format message to printout, ... Parameters like for printf()
     986              : @return CM_SUCCESS
     987              : */
     988            0 : INT cm_msg1(INT message_type, const char *filename, INT line,
     989              :             const char *facility, const char *routine, const char *format, ...) {
     990              :    va_list argptr;
     991            0 :    std::string message;
     992              :    static BOOL in_routine = FALSE;
     993              : 
     994              :    /* avoid recursive calles */
     995            0 :    if (in_routine)
     996            0 :       return 0;
     997              : 
     998            0 :    in_routine = TRUE;
     999              : 
    1000              :    /* print argument list into message */
    1001            0 :    va_start(argptr, format);
    1002            0 :    message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
    1003            0 :    va_end(argptr);
    1004              : 
    1005              :    /* call user function if set via cm_set_msg_print */
    1006            0 :    MessagePrintCallback f = _message_print.load();
    1007            0 :    if (f != NULL && (message_type & _message_mask_user) != 0)
    1008            0 :       (*f)(message.c_str());
    1009              : 
    1010              :    /* return if system mask is not set */
    1011            0 :    if ((message_type & _message_mask_system) == 0) {
    1012            0 :       in_routine = FALSE;
    1013            0 :       return CM_SUCCESS;
    1014              :    }
    1015              : 
    1016              :    /* send message to SYSMSG */
    1017            0 :    cm_msg_send_event(0, message_type, message.c_str());
    1018              : 
    1019              :    /* log message */
    1020            0 :    cm_msg_log(message_type, facility, message.c_str());
    1021              : 
    1022            0 :    in_routine = FALSE;
    1023              : 
    1024            0 :    return CM_SUCCESS;
    1025            0 : }
    1026              : 
    1027              : /********************************************************************/
    1028              : /**
    1029              : Register a dispatch function for receiving system messages.
    1030              : - example code from mlxspeaker.c
    1031              : \code
    1032              : void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
    1033              : {
    1034              :   char str[256], *pc, *sp;
    1035              :   // print message
    1036              :   printf("%s\n", (char *)(message));
    1037              : 
    1038              :   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
    1039              :                  ,header->event_id
    1040              :                  ,header->trigger_mask
    1041              :                  ,header->serial_number
    1042              :                  ,header->data_size);
    1043              :   pc = strchr((char *)(message),']')+2;
    1044              :   ...
    1045              :   // skip none talking message
    1046              :   if (header->trigger_mask == MT_TALK ||
    1047              :       header->trigger_mask == MT_USER)
    1048              :    ...
    1049              : }
    1050              : 
    1051              : int main(int argc, char *argv[])
    1052              : {
    1053              :   ...
    1054              :   // now connect to server
    1055              :   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
    1056              :   if (status != CM_SUCCESS)
    1057              :     return 1;
    1058              :   // Register callback for messages
    1059              :   cm_msg_register(receive_message);
    1060              :   ...
    1061              : }
    1062              : \endcode
    1063              : @param func Dispatch function.
    1064              : @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
    1065              : */
    1066            0 : INT cm_msg_register(EVENT_HANDLER *func) {
    1067              :    INT status, id;
    1068              : 
    1069              :    // we should only come here after the message buffer
    1070              :    // was opened by cm_connect_experiment()
    1071            0 :    assert(_msg_buffer);
    1072              : 
    1073            0 :    _msg_dispatch = func;
    1074              : 
    1075            0 :    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_NONBLOCKING, &id, func);
    1076              : 
    1077            0 :    return status;
    1078              : }
    1079              : 
    1080            0 : static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message) {
    1081            0 :    int new_message_length = strlen(new_message);
    1082            0 :    int new_allocated = 1024 + 2 * ((*allocated) + new_message_length);
    1083              :    char buf[100];
    1084              :    int buf_length;
    1085              : 
    1086              :    //printf("add_message: new message %d, length %d, new end: %d, allocated: %d, maybe reallocate size %d\n", new_message_length, *length, *length + new_message_length, *allocated, new_allocated);
    1087              : 
    1088            0 :    if (*length + new_message_length + 100 > *allocated) {
    1089            0 :       *messages = (char *) realloc(*messages, new_allocated);
    1090            0 :       assert(*messages != NULL);
    1091            0 :       *allocated = new_allocated;
    1092              :    }
    1093              : 
    1094            0 :    if (*length > 0)
    1095            0 :       if ((*messages)[(*length) - 1] != '\n') {
    1096            0 :          (*messages)[*length] = '\n'; // separator between messages
    1097            0 :          (*length) += 1;
    1098              :       }
    1099              : 
    1100            0 :    sprintf(buf, "%ld ", tstamp);
    1101            0 :    buf_length = strlen(buf);
    1102            0 :    memcpy(&((*messages)[*length]), buf, buf_length);
    1103            0 :    (*length) += buf_length;
    1104              : 
    1105            0 :    memcpy(&((*messages)[*length]), new_message, new_message_length);
    1106            0 :    (*length) += new_message_length;
    1107            0 :    (*messages)[*length] = 0; // make sure string is NUL terminated
    1108            0 : }
    1109              : 
    1110              : /* Retrieve message from an individual file. Internal use only */
    1111            0 : static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated,
    1112              :                             int *num_messages) {
    1113              :    BOOL stop;
    1114              :    int fh;
    1115              :    char *p, str[1000];
    1116              :    struct stat stat_buf;
    1117              :    time_t tstamp, tstamp_valid, tstamp_last;
    1118              : 
    1119            0 :    ss_tzset(); // required by localtime_r()
    1120              : 
    1121            0 :    *num_messages = 0;
    1122              : 
    1123            0 :    fh = open(filename, O_RDONLY | O_TEXT, 0644);
    1124            0 :    if (fh < 0) {
    1125            0 :       cm_msg(MERROR, "cm_msg_retrieve1", "Cannot open log file \"%s\", errno %d (%s)", filename, errno,
    1126            0 :              strerror(errno));
    1127            0 :       return SS_FILE_ERROR;
    1128              :    }
    1129              : 
    1130              :    /* read whole file into memory */
    1131            0 :    fstat(fh, &stat_buf);
    1132            0 :    ssize_t size = stat_buf.st_size;
    1133              : 
    1134              :    /* if file is too big, only read tail of file */
    1135            0 :    ssize_t maxsize = 10 * 1024 * 1024;
    1136            0 :    if (size > maxsize) {
    1137            0 :       lseek(fh, -maxsize, SEEK_END);
    1138              :       //printf("lseek status %d, errno %d (%s)\n", status, errno, strerror(errno));
    1139            0 :       size = maxsize;
    1140              :    }
    1141              : 
    1142            0 :    char *buffer = (char *) malloc(size + 1);
    1143              : 
    1144            0 :    if (buffer == NULL) {
    1145            0 :       cm_msg(MERROR, "cm_msg_retrieve1", "Cannot malloc %d bytes to read log file \"%s\", errno %d (%s)", (int) size,
    1146            0 :              filename, errno, strerror(errno));
    1147            0 :       close(fh);
    1148            0 :       return SS_FILE_ERROR;
    1149              :    }
    1150              : 
    1151            0 :    ssize_t rd = read(fh, buffer, size);
    1152              : 
    1153            0 :    if (rd != size) {
    1154            0 :       cm_msg(MERROR, "cm_msg_retrieve1", "Cannot read %d bytes from log file \"%s\", read() returned %d, errno %d (%s)",
    1155            0 :              (int) size, filename, (int) rd, errno, strerror(errno));
    1156            0 :       close(fh);
    1157            0 :       return SS_FILE_ERROR;
    1158              :    }
    1159              : 
    1160            0 :    buffer[size] = 0;
    1161            0 :    close(fh);
    1162              : 
    1163            0 :    p = buffer + size - 1;
    1164            0 :    tstamp_last = tstamp_valid = 0;
    1165            0 :    stop = FALSE;
    1166              : 
    1167            0 :    while (*p == '\n' || *p == '\r')
    1168            0 :       p--;
    1169              : 
    1170              :    int n;
    1171            0 :    for (n = 0; !stop && p > buffer;) {
    1172              : 
    1173              :       /* go to beginning of line */
    1174              :       int i;
    1175            0 :       for (i = 0; p != buffer && (*p != '\n' && *p != '\r'); i++)
    1176            0 :          p--;
    1177              : 
    1178              :       /* limit line length to sizeof(str) */
    1179            0 :       if (i >= (int) sizeof(str))
    1180            0 :          i = sizeof(str) - 1;
    1181              : 
    1182            0 :       if (p == buffer) {
    1183            0 :          i++;
    1184            0 :          memcpy(str, p, i);
    1185              :       } else
    1186            0 :          memcpy(str, p + 1, i);
    1187            0 :       str[i] = 0;
    1188            0 :       if (strchr(str, '\n'))
    1189            0 :          *strchr(str, '\n') = 0;
    1190            0 :       if (strchr(str, '\r'))
    1191            0 :          *strchr(str, '\r') = 0;
    1192            0 :       mstrlcat(str, "\n", sizeof(str));
    1193              : 
    1194              :       // extract time tag
    1195              :       time_t now;
    1196            0 :       time(&now);
    1197              : 
    1198              :       struct tm tms;
    1199            0 :       localtime_r(&now, &tms); // must call tzset() beforehand!
    1200              : 
    1201            0 :       if (str[0] >= '0' && str[0] <= '9') {
    1202              :          // new format
    1203            0 :          tms.tm_hour = atoi(str);
    1204            0 :          tms.tm_min = atoi(str + 3);
    1205            0 :          tms.tm_sec = atoi(str + 6);
    1206            0 :          tms.tm_year = atoi(str + 13) - 1900;
    1207            0 :          tms.tm_mon = atoi(str + 18) - 1;
    1208            0 :          tms.tm_mday = atoi(str + 21);
    1209              :       } else {
    1210              :          // old format
    1211            0 :          tms.tm_hour = atoi(str + 11);
    1212            0 :          tms.tm_min = atoi(str + 14);
    1213            0 :          tms.tm_sec = atoi(str + 17);
    1214            0 :          tms.tm_year = atoi(str + 20) - 1900;
    1215            0 :          for (i = 0; i < 12; i++)
    1216            0 :             if (strncmp(str + 4, mname[i], 3) == 0)
    1217            0 :                break;
    1218            0 :          tms.tm_mon = i;
    1219            0 :          tms.tm_mday = atoi(str + 8);
    1220              :       }
    1221            0 :       tstamp = ss_mktime(&tms);
    1222            0 :       if (tstamp != -1)
    1223            0 :          tstamp_valid = tstamp;
    1224              : 
    1225              :       // for new messages (n=0!), stop when t reached
    1226            0 :       if (n_messages == 0) {
    1227            0 :          if (tstamp_valid < t)
    1228            0 :             break;
    1229              :       }
    1230              : 
    1231              :       // for old messages, stop when all messages belonging to tstamp_last are sent
    1232            0 :       if (n_messages != 0) {
    1233            0 :          if (tstamp_last > 0 && tstamp_valid < tstamp_last)
    1234            0 :             break;
    1235              :       }
    1236              : 
    1237            0 :       if (t == 0 || tstamp == -1 ||
    1238            0 :           (n_messages > 0 && tstamp <= t) ||
    1239            0 :           (n_messages == 0 && tstamp >= t)) {
    1240              : 
    1241            0 :          n++;
    1242              : 
    1243            0 :          add_message(messages, length, allocated, tstamp, str);
    1244              :       }
    1245              : 
    1246            0 :       while (*p == '\n' || *p == '\r')
    1247            0 :          p--;
    1248              : 
    1249            0 :       if (n_messages == 1)
    1250            0 :          stop = TRUE;
    1251            0 :       else if (n_messages > 1) {
    1252              :          // continue collecting messages until time stamp differs from current one
    1253            0 :          if (n == n_messages)
    1254            0 :             tstamp_last = tstamp_valid;
    1255              : 
    1256              :          // if all messages without time tags, just return after n
    1257            0 :          if (n == n_messages && tstamp_valid == 0)
    1258            0 :             break;
    1259              :       }
    1260              :    }
    1261              : 
    1262            0 :    free(buffer);
    1263              : 
    1264            0 :    *num_messages = n;
    1265              : 
    1266            0 :    return CM_SUCCESS;
    1267              : }
    1268              : 
    1269              : /********************************************************************/
    1270              : /**
    1271              : Retrieve old messages from log file
    1272              : @param  facility         Logging facility ("midas", "chat", "lazy", ...)
    1273              : @param  t                Return messages logged before and including time t, value 0 means start with newest messages
    1274              : @param  min_messages     Minimum number of messages to return
    1275              : @param  messages         messages, newest first, separated by \n characters. caller should free() this buffer at the end.
    1276              : @param  num_messages     Number of messages returned
    1277              : @return CM_SUCCESS
    1278              : */
    1279            0 : INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages) {
    1280            0 :    std::string filename, linkname;
    1281              :    INT n, i;
    1282              :    time_t filedate;
    1283            0 :    int length = 0;
    1284            0 :    int allocated = 0;
    1285              : 
    1286            0 :    time(&filedate);
    1287            0 :    cm_msg_get_logfile(facility, filedate, &filename, &linkname, NULL);
    1288              : 
    1289              :    //printf("facility %s, filename \"%s\" \"%s\"\n", facility, filename, linkname);
    1290              : 
    1291              :    // see if file exists, use linkname if not
    1292            0 :    if (!linkname.empty()) {
    1293            0 :       if (!ss_file_exist(filename.c_str()))
    1294            0 :          filename = linkname;
    1295              :    }
    1296              : 
    1297            0 :    if (ss_file_exist(filename.c_str())) {
    1298            0 :       cm_msg_retrieve1(filename.c_str(), t, n_message, messages, &length, &allocated, &n);
    1299              :    } else {
    1300            0 :       n = 0;
    1301              :    }
    1302              : 
    1303              :    /* if there is no symlink, then there is no additional log files to read */
    1304            0 :    if (linkname.empty()) {
    1305            0 :       *num_messages = n;
    1306            0 :       return CM_SUCCESS;
    1307              :    }
    1308              : 
    1309              :    //printf("read more messages %d %d!\n", n, n_message);
    1310              : 
    1311            0 :    int missing = 0;
    1312            0 :    while (n < n_message) {
    1313            0 :       filedate -= 3600 * 24;         // go one day back
    1314              : 
    1315            0 :       cm_msg_get_logfile(facility, filedate, &filename, NULL, NULL);
    1316              : 
    1317              :       //printf("read [%s] for time %d!\n", filename.c_str(), filedate);
    1318              : 
    1319            0 :       if (ss_file_exist(filename.c_str())) {
    1320            0 :          cm_msg_retrieve1(filename.c_str(), t, n_message - n, messages, &length, &allocated, &i);
    1321            0 :          n += i;
    1322            0 :          missing = 0;
    1323              :       } else {
    1324            0 :          missing++;
    1325              :       }
    1326              : 
    1327              :       // stop if ten consecutive files are not found
    1328            0 :       if (missing > 10)
    1329            0 :          break;
    1330              :    }
    1331              : 
    1332            0 :    *num_messages = n;
    1333              : 
    1334            0 :    return CM_SUCCESS;
    1335            0 : }
    1336              : 
    1337              : /********************************************************************/
    1338              : /**
    1339              : Retrieve newest messages from "midas" facility log file
    1340              : @param  n_message        Number of messages to retrieve
    1341              : @param  message          buf_size bytes of messages, separated
    1342              :                          by \n characters. The returned number
    1343              :                          of bytes is normally smaller than the
    1344              :                          initial buf_size, since only full
    1345              :                          lines are returned.
    1346              : @param *buf_size         Size of message buffer to fill
    1347              : @return CM_SUCCESS, CM_TRUNCATED
    1348              : */
    1349            0 : INT cm_msg_retrieve(INT n_message, char *message, INT buf_size) {
    1350              :    int status;
    1351            0 :    char *messages = NULL;
    1352            0 :    int num_messages = 0;
    1353              : 
    1354            0 :    if (rpc_is_remote())
    1355            0 :       return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
    1356              : 
    1357            0 :    status = cm_msg_retrieve2("midas", 0, n_message, &messages, &num_messages);
    1358              : 
    1359            0 :    if (messages) {
    1360            0 :       mstrlcpy(message, messages, buf_size);
    1361            0 :       int len = strlen(messages);
    1362            0 :       if (len > buf_size)
    1363            0 :          status = CM_TRUNCATED;
    1364            0 :       free(messages);
    1365              :    }
    1366              : 
    1367            0 :    return status;
    1368              : }
    1369              : 
    1370              : /**dox***************************************************************/
    1371              : /** @} *//* end of msgfunctionc */
    1372              : 
    1373              : /**dox***************************************************************/
    1374              : /** @addtogroup cmfunctionc
    1375              :  *
    1376              :  *  @{  */
    1377              : 
    1378              : /********************************************************************/
    1379              : /**
    1380              : Get time from MIDAS server and set local time.
    1381              : @param    seconds         Time in seconds
    1382              : @return CM_SUCCESS
    1383              : */
    1384            0 : INT cm_synchronize(DWORD *seconds) {
    1385              :    INT sec, status;
    1386              : 
    1387              :    /* if connected to server, get time from there */
    1388            0 :    if (rpc_is_remote()) {
    1389            0 :       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
    1390              : 
    1391              :       /* set local time */
    1392            0 :       if (status == CM_SUCCESS)
    1393            0 :          ss_settime(sec);
    1394              :    }
    1395              : 
    1396              :    /* return time to caller */
    1397            0 :    if (seconds != NULL) {
    1398            0 :       *seconds = ss_time();
    1399              :    }
    1400              : 
    1401            0 :    return CM_SUCCESS;
    1402              : }
    1403              : 
    1404              : /********************************************************************/
    1405              : /**
    1406              : Get time from MIDAS server and set local time.
    1407              : @param    str            return time string
    1408              : @param    buf_size       Maximum size of str
    1409              : @return   CM_SUCCESS
    1410              : */
    1411            0 : INT cm_asctime(char *str, INT buf_size) {
    1412              :    /* if connected to server, get time from there */
    1413            0 :    if (rpc_is_remote())
    1414            0 :       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
    1415              : 
    1416              :    /* return local time */
    1417            0 :    mstrlcpy(str, ss_asctime().c_str(), buf_size);
    1418              : 
    1419            0 :    return CM_SUCCESS;
    1420              : }
    1421              : 
    1422              : /********************************************************************/
    1423              : /**
    1424              : Get time from MIDAS server and set local time.
    1425              : @return   return time string
    1426              : */
    1427            0 : std::string cm_asctime() {
    1428              :    /* if connected to server, get time from there */
    1429            0 :    if (rpc_is_remote()) {
    1430              :       char buf[256];
    1431            0 :       int status = rpc_call(RPC_CM_ASCTIME, buf, sizeof(buf));
    1432            0 :       if (status == CM_SUCCESS) {
    1433            0 :          return buf;
    1434              :       } else {
    1435            0 :          return "";
    1436              :       }
    1437              :    }
    1438              : 
    1439              :    /* return local time */
    1440            0 :    return ss_asctime();
    1441              : }
    1442              : 
    1443              : /********************************************************************/
    1444              : /**
    1445              : Get time from ss_time on server.
    1446              : @param    t string
    1447              : @return   CM_SUCCESS
    1448              : */
    1449            0 : INT cm_time(DWORD *t) {
    1450              :    /* if connected to server, get time from there */
    1451            0 :    if (rpc_is_remote())
    1452            0 :       return rpc_call(RPC_CM_TIME, t);
    1453              : 
    1454              :    /* return local time */
    1455            0 :    *t = ss_time();
    1456              : 
    1457            0 :    return CM_SUCCESS;
    1458              : }
    1459              : 
    1460              : /**dox***************************************************************/
    1461              : /** @} *//* end of cmfunctionc */
    1462              : 
    1463              : /********************************************************************\
    1464              : *                                                                    *
    1465              : *           cm_xxx  -  Common Functions to buffer & database         *
    1466              : *                                                                    *
    1467              : \********************************************************************/
    1468              : 
    1469              : /* Globals */
    1470              : 
    1471              : static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
    1472              : static HNDLE _hDB = 0;          /* Database handle */
    1473              : static std::string _experiment_name;
    1474              : static std::string _client_name;
    1475              : static std::string _path_name;
    1476              : static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
    1477              : INT _semaphore_alarm = -1;
    1478              : INT _semaphore_elog = -1;
    1479              : INT _semaphore_history = -1;
    1480              : //INT _semaphore_msg = -1;
    1481              : 
    1482              : /**dox***************************************************************/
    1483              : /** @addtogroup cmfunctionc
    1484              :  *
    1485              :  *  @{  */
    1486              : 
    1487              : /**
    1488              : Return version number of current MIDAS library as a string
    1489              : @return version number
    1490              : */
    1491            0 : const char *cm_get_version() {
    1492            0 :    return MIDAS_VERSION;
    1493              : }
    1494              : 
    1495              : /**
    1496              : Return git revision number of current MIDAS library as a string
    1497              : @return revision number
    1498              : */
    1499            0 : const char *cm_get_revision() {
    1500            0 :    return GIT_REVISION;
    1501              : }
    1502              : 
    1503              : /********************************************************************/
    1504              : /**
    1505              : Set path to actual experiment. This function gets called
    1506              : by cm_connect_experiment if the connection is established
    1507              : to a local experiment (not through the TCP/IP server).
    1508              : The path is then used for all shared memory routines.
    1509              : @param  path             Pathname
    1510              : @return CM_SUCCESS
    1511              : */
    1512            3 : INT cm_set_path(const char *path) {
    1513            3 :    assert(path);
    1514            3 :    assert(path[0] != 0);
    1515              : 
    1516            3 :    _path_name = path;
    1517              : 
    1518            3 :    if (_path_name.back() != DIR_SEPARATOR) {
    1519            0 :       _path_name += DIR_SEPARATOR_STR;
    1520              :    }
    1521              : 
    1522              :    //printf("cm_set_path [%s]\n", _path_name.c_str());
    1523              : 
    1524            3 :    return CM_SUCCESS;
    1525              : }
    1526              : 
    1527              : /********************************************************************/
    1528              : /**
    1529              : Return the path name previously set with cm_set_path.
    1530              : @param  path             Pathname
    1531              : @return CM_SUCCESS
    1532              : */
    1533            0 : INT cm_get_path(char *path, int path_size) {
    1534              :    // check that we were not accidentally called
    1535              :    // with the size of the pointer to a string
    1536              :    // instead of the size of the string buffer
    1537            0 :    assert(path_size != sizeof(char *));
    1538            0 :    assert(path);
    1539            0 :    assert(_path_name.length() > 0);
    1540              : 
    1541            0 :    mstrlcpy(path, _path_name.c_str(), path_size);
    1542              : 
    1543            0 :    return CM_SUCCESS;
    1544              : }
    1545              : 
    1546              : /********************************************************************/
    1547              : /**
    1548              : Return the path name previously set with cm_set_path.
    1549              : @param  path             Pathname
    1550              : @return CM_SUCCESS
    1551              : */
    1552           85 : std::string cm_get_path() {
    1553           85 :    assert(_path_name.length() > 0);
    1554           85 :    return _path_name;
    1555              : }
    1556              : 
    1557              : /********************************************************************/
    1558              : /* C++ wrapper for cm_get_path */
    1559              : 
    1560            0 : INT EXPRT cm_get_path_string(std::string *path) {
    1561            0 :    assert(path != NULL);
    1562            0 :    assert(_path_name.length() > 0);
    1563            0 :    *path = _path_name;
    1564            0 :    return CM_SUCCESS;
    1565              : }
    1566              : 
    1567              : /********************************************************************/
    1568              : /**
    1569              : Set name of the experiment
    1570              : @param  name             Experiment name
    1571              : @return CM_SUCCESS
    1572              : */
    1573            3 : INT cm_set_experiment_name(const char *name) {
    1574            3 :    _experiment_name = name;
    1575            3 :    return CM_SUCCESS;
    1576              : }
    1577              : 
    1578              : /********************************************************************/
    1579              : /**
    1580              : Return the experiment name
    1581              : @param  name             Pointer to user string, size should be at least NAME_LENGTH
    1582              : @param  name_size        Size of user string
    1583              : @return CM_SUCCESS
    1584              : */
    1585            0 : INT cm_get_experiment_name(char *name, int name_length) {
    1586            0 :    mstrlcpy(name, _experiment_name.c_str(), name_length);
    1587            0 :    return CM_SUCCESS;
    1588              : }
    1589              : 
    1590              : /********************************************************************/
    1591              : /**
    1592              : Return the experiment name
    1593              : @return experiment name
    1594              : */
    1595           17 : std::string cm_get_experiment_name() {
    1596           17 :    return _experiment_name;
    1597              : }
    1598              : 
    1599              : /**dox***************************************************************/
    1600              : /** @} *//* end of cmfunctionc */
    1601              : 
    1602              : /**dox***************************************************************/
    1603              : /** @addtogroup cmfunctionc
    1604              :  *
    1605              :  *  @{  */
    1606              : 
    1607              : #ifdef LOCAL_ROUTINES
    1608              : 
    1609              : struct exptab_entry {
    1610              :    std::string name;
    1611              :    std::string directory;
    1612              :    std::string user;
    1613              : };
    1614              : 
    1615              : struct exptab_struct {
    1616              :    std::string filename;
    1617              :    std::vector<exptab_entry> exptab;
    1618              : };
    1619              : 
    1620              : static exptab_struct _exptab; // contents of exptab file
    1621              : 
    1622              : /**
    1623              : Scan the "exptab" file for MIDAS experiment names and save them
    1624              : for later use by rpc_server_accept(). The file is first searched
    1625              : under $MIDAS/exptab if present, then the directory from argv[0] is probed.
    1626              : @return CM_SUCCESS<br>
    1627              :         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
    1628              : */
    1629            2 : INT cm_read_exptab(exptab_struct *exptab)
    1630              : {
    1631            2 :    exptab->exptab.clear();
    1632              : 
    1633              :    /* MIDAS_DIR overrides exptab */
    1634            2 :    if (getenv("MIDAS_DIR")) {
    1635            0 :       exptab->filename = "MIDAS_DIR";
    1636              : 
    1637            0 :       exptab_entry e;
    1638              : 
    1639            0 :       if (getenv("MIDAS_EXPT_NAME")) {
    1640            0 :          e.name = getenv("MIDAS_EXPT_NAME");
    1641              :       } else {
    1642            0 :          e.name = "Default";
    1643            0 :          cm_msg(MERROR, "cm_read_exptab", "Experiments that use MIDAS_DIR must also set MIDAS_EXPT_NAME to the name of the experiment! Using experiment name \"%s\"", e.name.c_str());
    1644              :       }
    1645              : 
    1646            0 :       e.directory = getenv("MIDAS_DIR");
    1647            0 :       e.user = "";
    1648              : 
    1649            0 :       exptab->exptab.push_back(e);
    1650              : 
    1651            0 :       return CM_SUCCESS;
    1652            0 :    }
    1653              : 
    1654              :    /* default directory for different OSes */
    1655              : #if defined (OS_WINNT)
    1656              :    std::string str;
    1657              :    if (getenv("SystemRoot"))
    1658              :       str = getenv("SystemRoot");
    1659              :    else if (getenv("windir"))
    1660              :       str = getenv("windir");
    1661              :    else
    1662              :       str = "";
    1663              : 
    1664              :    std::string alt_str = str;
    1665              :    str += "\\system32\\exptab";
    1666              :    alt_str += "\\system\\exptab";
    1667              : #elif defined (OS_UNIX)
    1668            4 :    std::string str = "/etc/exptab";
    1669            2 :    std::string alt_str = "/exptab";
    1670              : #else
    1671              :    std::strint str = "exptab";
    1672              :    std::string alt_str = "exptab";
    1673              : #endif
    1674              : 
    1675              :    /* MIDAS_EXPTAB overrides default directory */
    1676            2 :    if (getenv("MIDAS_EXPTAB")) {
    1677            2 :       str = getenv("MIDAS_EXPTAB");
    1678            2 :       alt_str = getenv("MIDAS_EXPTAB");
    1679              :    }
    1680              :    
    1681            2 :    exptab->filename = str;
    1682              : 
    1683              :    /* read list of available experiments */
    1684            2 :    FILE* f = fopen(str.c_str(), "r");
    1685            2 :    if (f == NULL) {
    1686            0 :       f = fopen(alt_str.c_str(), "r");
    1687            0 :       if (f == NULL)
    1688            0 :          return CM_UNDEF_ENVIRON;
    1689            0 :       exptab->filename = alt_str;
    1690              :    }
    1691              : 
    1692            2 :    if (f != NULL) {
    1693              :       do {
    1694              :          char buf[256];
    1695            4 :          memset(buf, 0, sizeof(buf));
    1696            4 :          char* str = fgets(buf, sizeof(buf)-1, f);
    1697            4 :          if (str == NULL)
    1698            2 :             break;
    1699            2 :          if (str[0] == 0) continue; // empty line
    1700            2 :          if (str[0] == '#') continue; // comment line
    1701              : 
    1702            2 :          exptab_entry e;
    1703              : 
    1704              :          // following code emulates the function of this sprintf():
    1705              :          //sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
    1706              : 
    1707              :          // skip leading spaces
    1708            2 :          while (*str && isspace(*str))
    1709            0 :             str++;
    1710              : 
    1711            2 :          char* p1 = str;
    1712            2 :          char* p2 = str;
    1713              : 
    1714           18 :          while (*p2 && !isspace(*p2))
    1715           16 :             p2++;
    1716              : 
    1717            2 :          ssize_t len = p2-p1;
    1718              : 
    1719            2 :          if (len<1)
    1720            0 :             continue;
    1721              :          
    1722              :          //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
    1723              : 
    1724            2 :          e.name = std::string(p1, len);
    1725              : 
    1726            2 :          if (*p2 == 0)
    1727            0 :             continue;
    1728              : 
    1729            2 :          str = p2;
    1730              : 
    1731              :          // skip leading spaces
    1732            4 :          while (*str && isspace(*str))
    1733            2 :             str++;
    1734              :          
    1735            2 :          p1 = str;
    1736            2 :          p2 = str;
    1737              : 
    1738           78 :          while (*p2 && !isspace(*p2))
    1739           76 :             p2++;
    1740              : 
    1741            2 :          len = p2-p1;
    1742              : 
    1743            2 :          if (len<1)
    1744            0 :             continue;
    1745              :          
    1746              :          //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
    1747              : 
    1748            2 :          e.directory = std::string(p1, len);
    1749              : 
    1750            2 :          if (*p2 == 0)
    1751            0 :             continue;
    1752              : 
    1753            2 :          str = p2;
    1754              : 
    1755              :          // skip leading spaces
    1756            4 :          while (*str && isspace(*str))
    1757            2 :             str++;
    1758              :          
    1759            2 :          p1 = str;
    1760            2 :          p2 = str;
    1761              : 
    1762           18 :          while (*p2 && !isspace(*p2))
    1763           16 :             p2++;
    1764              : 
    1765            2 :          len = p2-p1;
    1766              : 
    1767              :          //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
    1768              : 
    1769            2 :          e.user = std::string(p1, len);
    1770              :          
    1771              :          /* check for trailing directory separator */
    1772            2 :          if (!ends_with_char(e.directory, DIR_SEPARATOR)) {
    1773            2 :             e.directory += DIR_SEPARATOR_STR;
    1774              :          }
    1775              :          
    1776            2 :          exptab->exptab.push_back(e);
    1777            4 :       } while (!feof(f));
    1778            2 :       fclose(f);
    1779              :    }
    1780              : 
    1781              : #if 0
    1782              :    cm_msg(MINFO, "cm_read_exptab", "Read exptab \"%s\":", exptab->filename.c_str()); 
    1783              :    for (unsigned j=0; j<exptab->exptab.size(); j++) {
    1784              :       cm_msg(MINFO, "cm_read_exptab", "entry %d, experiment \"%s\", directory \"%s\", user \"%s\"", j, exptab->exptab[j].name.c_str(), exptab->exptab[j].directory.c_str(), exptab->exptab[j].user.c_str());
    1785              :    }
    1786              : #endif
    1787              : 
    1788            2 :    return CM_SUCCESS;
    1789            2 : }
    1790              : 
    1791              : /********************************************************************/
    1792              : /**
    1793              : Return location of exptab file
    1794              : @param s               Pointer to string buffer
    1795              : @param size            Size of string buffer
    1796              : @return CM_SUCCESS
    1797              : */
    1798            0 : int cm_get_exptab_filename(char *s, int size) {
    1799            0 :    mstrlcpy(s, _exptab.filename.c_str(), size);
    1800            0 :    return CM_SUCCESS;
    1801              : }
    1802              : 
    1803            1 : std::string cm_get_exptab_filename() {
    1804            1 :    return _exptab.filename;
    1805              : }
    1806              : 
    1807              : /********************************************************************/
    1808              : /**
    1809              : Return exptab information for given experiment
    1810              : @param s               Pointer to string buffer
    1811              : @param size            Size of string buffer
    1812              : @return CM_SUCCESS
    1813              : */
    1814            3 : int cm_get_exptab(const char *expname, std::string* dir, std::string* user) {
    1815              : 
    1816            3 :    if (_exptab.exptab.size() == 0) {
    1817            0 :       int status = cm_read_exptab(&_exptab);
    1818            0 :       if (status != CM_SUCCESS)
    1819            0 :          return status;
    1820              :    }
    1821              : 
    1822            3 :    for (unsigned i = 0; i < _exptab.exptab.size(); i++) {
    1823            3 :       if (_exptab.exptab[i].name == expname) {
    1824            3 :          if (dir)
    1825            3 :             *dir = _exptab.exptab[i].directory;
    1826            3 :          if (user)
    1827            3 :             *user = _exptab.exptab[i].user;
    1828            3 :          return CM_SUCCESS;
    1829              :       }
    1830              :    }
    1831            0 :    if (dir)
    1832            0 :       *dir = "";
    1833            0 :    if (user)
    1834            0 :       *user = "";
    1835            0 :    return CM_UNDEF_EXP;
    1836              : }
    1837              : 
    1838              : /********************************************************************/
    1839              : /**
    1840              : Return exptab information for given experiment
    1841              : @param s               Pointer to string buffer
    1842              : @param size            Size of string buffer
    1843              : @return CM_SUCCESS
    1844              : */
    1845            0 : int cm_get_exptab(const char *expname, char *dir, int dir_size, char *user, int user_size) {
    1846            0 :    std::string sdir, suser;
    1847            0 :    int status = cm_get_exptab(expname, &sdir, &suser);
    1848            0 :    if (status == CM_SUCCESS) {
    1849            0 :       if (dir)
    1850            0 :          mstrlcpy(dir, sdir.c_str(), dir_size);
    1851            0 :       if (user)
    1852            0 :          mstrlcpy(user, suser.c_str(), user_size);
    1853            0 :       return CM_SUCCESS;
    1854              :    }
    1855            0 :    return CM_UNDEF_EXP;
    1856            0 : }
    1857              : 
    1858              : #endif // LOCAL_ROUTINES
    1859              : 
    1860              : /********************************************************************/
    1861              : /**
    1862              : Delete client info from database
    1863              : @param hDB               Database handle
    1864              : @param pid               PID of entry to delete, zero for this process.
    1865              : @return CM_SUCCESS
    1866              : */
    1867            2 : INT cm_delete_client_info(HNDLE hDB, INT pid) {
    1868              :    /* only do it if local */
    1869            2 :    if (!rpc_is_remote()) {
    1870            2 :       db_delete_client_info(hDB, pid);
    1871              :    }
    1872            2 :    return CM_SUCCESS;
    1873              : }
    1874              : 
    1875              : /********************************************************************/
    1876              : /**
    1877              : Check if a client with a /system/client/xxx entry has
    1878              : a valid entry in the ODB client table. If not, remove
    1879              : that client from the /system/client tree.
    1880              : @param   hDB               Handle to online database
    1881              : @param   hKeyClient        Handle to client key
    1882              : @return  CM_SUCCESS, CM_NO_CLIENT
    1883              : */
    1884            0 : INT cm_check_client(HNDLE hDB, HNDLE hKeyClient) {
    1885            0 :    if (rpc_is_remote())
    1886            0 :       return rpc_call(RPC_CM_CHECK_CLIENT, hDB, hKeyClient);
    1887              : 
    1888              : #ifdef LOCAL_ROUTINES
    1889            0 :    return db_check_client(hDB, hKeyClient);
    1890              : #endif                          /*LOCAL_ROUTINES */
    1891              :    return CM_SUCCESS;
    1892              : }
    1893              : 
    1894              : /********************************************************************/
    1895              : /**
    1896              : Set client information in online database and return handle
    1897              : @param  hDB              Handle to online database
    1898              : @param  hKeyClient       returned key
    1899              : @param  host_name        server name
    1900              : @param  client_name      Name of this program as it will be seen
    1901              :                          by other clients.
    1902              : @param  hw_type          Type of byte order
    1903              : @param  password         MIDAS password
    1904              : @param  watchdog_timeout Default watchdog timeout, can be overwritten
    1905              :                          by ODB setting /programs/\<name\>/Watchdog timeout
    1906              : @return   CM_SUCCESS
    1907              : */
    1908            2 : INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name,
    1909              :                        char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout) {
    1910            2 :    if (rpc_is_remote())
    1911            0 :       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
    1912            0 :                       host_name, client_name, hw_type, password, watchdog_timeout);
    1913              : 
    1914              : #ifdef LOCAL_ROUTINES
    1915              :    {
    1916              :       INT status, pid, data, i, idx, size;
    1917              :       HNDLE hKey, hSubkey;
    1918              :       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
    1919              :       BOOL call_watchdog, allow;
    1920            2 :       PROGRAM_INFO_STR(program_info_str);
    1921              : 
    1922              :       /* check security if password is present */
    1923            2 :       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
    1924            2 :       if (hKey) {
    1925              :          /* get password */
    1926            0 :          size = sizeof(pwd);
    1927            0 :          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
    1928              : 
    1929              :          /* first check allowed hosts list */
    1930            0 :          allow = FALSE;
    1931            0 :          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
    1932            0 :          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
    1933            0 :             allow = TRUE;
    1934              : 
    1935              :          /* check allowed programs list */
    1936            0 :          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
    1937            0 :          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
    1938            0 :             allow = TRUE;
    1939              : 
    1940              :          /* now check password */
    1941            0 :          if (!allow && strcmp(password, pwd) != 0) {
    1942            0 :             if (password[0])
    1943            0 :                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
    1944            0 :             return CM_WRONG_PASSWORD;
    1945              :          }
    1946              :       }
    1947              : 
    1948              :       /* make following operation atomic by locking database */
    1949            2 :       db_lock_database(hDB);
    1950              : 
    1951              :       /* check if entry with this pid exists already */
    1952            2 :       pid = ss_getpid();
    1953              : 
    1954            2 :       sprintf(str, "System/Clients/%0d", pid);
    1955            2 :       status = db_find_key(hDB, 0, str, &hKey);
    1956            2 :       if (status == DB_SUCCESS) {
    1957            0 :          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
    1958            0 :          db_delete_key(hDB, hKey, TRUE);
    1959              :       }
    1960              : 
    1961            2 :       if (strlen(client_name) >= NAME_LENGTH)
    1962            0 :          client_name[NAME_LENGTH] = 0;
    1963              : 
    1964            2 :       strcpy(name, client_name);
    1965            2 :       strcpy(orig_name, client_name);
    1966              : 
    1967              :       /* check if client name already exists */
    1968            2 :       status = db_find_key(hDB, 0, "System/Clients", &hKey);
    1969              : 
    1970            4 :       for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
    1971            2 :          for (i = 0;; i++) {
    1972            4 :             status = db_enum_key(hDB, hKey, i, &hSubkey);
    1973            4 :             if (status == DB_NO_MORE_SUBKEYS)
    1974            2 :                break;
    1975              : 
    1976            2 :             if (status == DB_SUCCESS) {
    1977            2 :                size = sizeof(str);
    1978            2 :                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, FALSE);
    1979            2 :                if (status != DB_SUCCESS)
    1980            2 :                   continue;
    1981              :             }
    1982              : 
    1983              :             /* check if client is living */
    1984            0 :             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
    1985            0 :                continue;
    1986              : 
    1987            0 :             if (equal_ustring(str, name)) {
    1988            0 :                sprintf(name, "%s%d", client_name, idx);
    1989            0 :                break;
    1990              :             }
    1991              :          }
    1992              :       }
    1993              : 
    1994              :       /* set name */
    1995            2 :       sprintf(str, "System/Clients/%0d/Name", pid);
    1996            2 :       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
    1997            2 :       if (status != DB_SUCCESS) {
    1998            0 :          db_unlock_database(hDB);
    1999            0 :          cm_msg(MERROR, "cm_set_client_info", "cannot set client name, db_set_value(%s) status %d", str, status);
    2000            0 :          return status;
    2001              :       }
    2002              : 
    2003              :       /* copy new client name */
    2004            2 :       strcpy(client_name, name);
    2005            2 :       db_set_client_name(hDB, client_name);
    2006              : 
    2007              :       /* set also as rpc name */
    2008            2 :       rpc_set_name(client_name);
    2009              : 
    2010              :       /* use /system/clients/PID as root */
    2011            2 :       sprintf(str, "System/Clients/%0d", pid);
    2012            2 :       db_find_key(hDB, 0, str, &hKey);
    2013              : 
    2014              :       /* set host name */
    2015            2 :       status = db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
    2016            2 :       if (status != DB_SUCCESS) {
    2017            0 :          db_unlock_database(hDB);
    2018            0 :          return status;
    2019              :       }
    2020              : 
    2021              :       /* set computer id */
    2022            2 :       status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT32);
    2023            2 :       if (status != DB_SUCCESS) {
    2024            0 :          db_unlock_database(hDB);
    2025            0 :          return status;
    2026              :       }
    2027              : 
    2028              :       /* set server port */
    2029            2 :       data = 0;
    2030            2 :       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT32);
    2031            2 :       if (status != DB_SUCCESS) {
    2032            0 :          db_unlock_database(hDB);
    2033            0 :          return status;
    2034              :       }
    2035              : 
    2036              :       /* lock client entry */
    2037            2 :       db_set_mode(hDB, hKey, MODE_READ, TRUE);
    2038              : 
    2039              :       /* get (set) default watchdog timeout */
    2040            2 :       size = sizeof(watchdog_timeout);
    2041            2 :       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
    2042            2 :       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
    2043              : 
    2044              :       /* define /programs entry */
    2045            2 :       sprintf(str, "/Programs/%s", orig_name);
    2046            2 :       db_create_record(hDB, 0, str, strcomb1(program_info_str).c_str());
    2047              : 
    2048              :       /* save handle for ODB and client */
    2049            2 :       cm_set_experiment_database(hDB, hKey);
    2050              : 
    2051              :       /* save watchdog timeout */
    2052            2 :       cm_get_watchdog_params(&call_watchdog, NULL);
    2053            2 :       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
    2054              : 
    2055              :       /* end of atomic operations */
    2056            2 :       db_unlock_database(hDB);
    2057              : 
    2058              :       /* touch notify key to inform others */
    2059            2 :       data = 0;
    2060            2 :       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32);
    2061              : 
    2062            2 :       *hKeyClient = hKey;
    2063              :    }
    2064              : #endif                          /* LOCAL_ROUTINES */
    2065              : 
    2066            2 :    return CM_SUCCESS;
    2067              : }
    2068              : 
    2069              : /********************************************************************/
    2070              : /**
    2071              : Get current client name
    2072              : @return   current client name
    2073              : */
    2074            2 : std::string cm_get_client_name()
    2075              : {
    2076              :    INT status;
    2077              :    HNDLE hDB, hKey;
    2078              : 
    2079              :    /* get root key of client */
    2080            2 :    cm_get_experiment_database(&hDB, &hKey);
    2081            2 :    if (!hDB) {
    2082            0 :       return "unknown";
    2083              :    }
    2084              : 
    2085            2 :    std::string name;
    2086              : 
    2087            2 :    status = db_get_value_string(hDB, hKey, "Name", 0, &name);
    2088            2 :    if (status != DB_SUCCESS) {
    2089            0 :       return "unknown";
    2090              :    }
    2091              : 
    2092              :    //printf("get client name: [%s]\n", name.c_str());
    2093              : 
    2094            2 :    return name;
    2095            2 : }
    2096              : 
    2097              : /********************************************************************/
    2098              : /**
    2099              : Returns MIDAS environment variables.
    2100              : @attention This function can be used to evaluate the standard MIDAS
    2101              :            environment variables before connecting to an experiment
    2102              :            (see @ref Environment_variables).
    2103              :            The usual way is that the host name and experiment name are first derived
    2104              :            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
    2105              :            They can then be superseded by command line parameters with -h and -e flags.
    2106              : \code
    2107              : #include <stdio.h>
    2108              : #include <midas.h>
    2109              : main(int argc, char *argv[])
    2110              : {
    2111              :   INT  status, i;
    2112              :   char host_name[256],exp_name[32];
    2113              : 
    2114              :   // get default values from environment
    2115              :   cm_get_environment(host_name, exp_name);
    2116              : 
    2117              :   // parse command line parameters
    2118              :   for (i=1 ; i<argc ; i++)
    2119              :     {
    2120              :     if (argv[i][0] == '-')
    2121              :       {
    2122              :       if (i+1 >= argc || argv[i+1][0] == '-')
    2123              :         goto usage;
    2124              :       if (argv[i][1] == 'e')
    2125              :         strcpy(exp_name, argv[++i]);
    2126              :       else if (argv[i][1] == 'h')
    2127              :         strcpy(host_name, argv[++i]);
    2128              :       else
    2129              :         {
    2130              : usage:
    2131              :         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
    2132              :         return 1;
    2133              :         }
    2134              :       }
    2135              :     }
    2136              :   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
    2137              :   if (status != CM_SUCCESS)
    2138              :     return 1;
    2139              :     ...do anyting...
    2140              :   cm_disconnect_experiment();
    2141              : }
    2142              : \endcode
    2143              : @param host_name          Contents of MIDAS_SERVER_HOST environment variable.
    2144              : @param host_name_size     string length
    2145              : @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
    2146              : @param exp_name_size      string length
    2147              : @return CM_SUCCESS
    2148              : */
    2149            1 : INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size) {
    2150            1 :    if (host_name)
    2151            1 :       host_name[0] = 0;
    2152            1 :    if (exp_name)
    2153            1 :       exp_name[0] = 0;
    2154              : 
    2155            1 :    if (host_name && getenv("MIDAS_SERVER_HOST"))
    2156            0 :       mstrlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
    2157              : 
    2158            1 :    if (exp_name && getenv("MIDAS_EXPT_NAME"))
    2159            0 :       mstrlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
    2160              : 
    2161            1 :    return CM_SUCCESS;
    2162              : }
    2163              : 
    2164            1 : INT cm_get_environment(std::string *host_name, std::string *exp_name) {
    2165            1 :    if (host_name)
    2166            1 :       *host_name = "";
    2167            1 :    if (exp_name)
    2168            1 :       *exp_name = "";
    2169              : 
    2170            1 :    if (host_name && getenv("MIDAS_SERVER_HOST"))
    2171            0 :       *host_name = getenv("MIDAS_SERVER_HOST");
    2172              : 
    2173            1 :    if (exp_name && getenv("MIDAS_EXPT_NAME"))
    2174            0 :       *exp_name = getenv("MIDAS_EXPT_NAME");
    2175              : 
    2176            1 :    return CM_SUCCESS;
    2177              : }
    2178              : 
    2179              : #ifdef LOCAL_ROUTINES
    2180              : 
    2181            2 : int cm_set_experiment_local(const char* exp_name)
    2182              : {
    2183            2 :    std::string exp_name1;
    2184              : 
    2185            2 :    if ((exp_name != NULL) && (strlen(exp_name) > 0)) {
    2186            1 :       exp_name1 = exp_name;
    2187              :    } else {
    2188            1 :       int status = cm_select_experiment_local(&exp_name1);
    2189            1 :       if (status != CM_SUCCESS)
    2190            0 :          return status;
    2191              :    }
    2192              : 
    2193            2 :    std::string expdir, expuser;
    2194              :    
    2195            2 :    int status = cm_get_exptab(exp_name1.c_str(), &expdir, &expuser);
    2196              :    
    2197            2 :    if (status != CM_SUCCESS) {
    2198            0 :       cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" not found in exptab file \"%s\"", exp_name1.c_str(), cm_get_exptab_filename().c_str());
    2199            0 :       return CM_UNDEF_EXP;
    2200              :    }
    2201              : 
    2202            2 :    if (!ss_dir_exist(expdir.c_str())) {
    2203            0 :       cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" directory \"%s\" does not exist", exp_name1.c_str(), expdir.c_str());
    2204            0 :       return CM_UNDEF_EXP;
    2205              :    }
    2206              :    
    2207            2 :    cm_set_experiment_name(exp_name1.c_str());
    2208            2 :    cm_set_path(expdir.c_str());
    2209              : 
    2210            2 :    return CM_SUCCESS;
    2211            2 : }
    2212              : 
    2213              : #endif // LOCAL_ROUTINES
    2214              :    
    2215              : /********************************************************************/
    2216            2 : void cm_check_connect(void) {
    2217            2 :    if (_hKeyClient) {
    2218            0 :       cm_msg(MERROR, "cm_check_connect", "cm_disconnect_experiment not called at end of program");
    2219            0 :       cm_msg_flush_buffer();
    2220              :    }
    2221            2 : }
    2222              : 
    2223              : /********************************************************************/
    2224              : /**
    2225              : This function connects to an existing MIDAS experiment.
    2226              : This must be the first call in a MIDAS application.
    2227              : It opens three TCP connection to the remote host (one for RPC calls,
    2228              : one to send events and one for hot-link notifications from the remote host)
    2229              : and writes client information into the ODB under /System/Clients.
    2230              : @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
    2231              : and MIDAS_EXPT_NAME environment variables as defaults to the host name and
    2232              : experiment name (see @ref Environment_variables).
    2233              : For that purpose, the function cm_get_environment()
    2234              : should be called prior to cm_connect_experiment(). If command line
    2235              : parameters -h and -e are used, the evaluation should be done between
    2236              : cm_get_environment() and cm_connect_experiment(). The function
    2237              : cm_disconnect_experiment() must be called before a MIDAS application exits.
    2238              : \code
    2239              : #include <stdio.h>
    2240              : #include <midas.h>
    2241              : main(int argc, char *argv[])
    2242              : {
    2243              :   INT  status, i;
    2244              :   char host_name[256],exp_name[32];
    2245              : 
    2246              :   // get default values from environment
    2247              :   cm_get_environment(host_name, exp_name);
    2248              : 
    2249              :   // parse command line parameters
    2250              :   for (i=1 ; i<argc ; i++)
    2251              :     {
    2252              :     if (argv[i][0] == '-')
    2253              :       {
    2254              :       if (i+1 >= argc || argv[i+1][0] == '-')
    2255              :         goto usage;
    2256              :       if (argv[i][1] == 'e')
    2257              :         strcpy(exp_name, argv[++i]);
    2258              :       else if (argv[i][1] == 'h')
    2259              :         strcpy(host_name, argv[++i]);
    2260              :       else
    2261              :         {
    2262              : usage:
    2263              :         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
    2264              :         return 1;
    2265              :         }
    2266              :       }
    2267              :     }
    2268              :   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
    2269              :   if (status != CM_SUCCESS)
    2270              :     return 1;
    2271              :   ...do operations...
    2272              :   cm_disconnect_experiment();
    2273              : }
    2274              : \endcode
    2275              : @param host_name Specifies host to connect to. Must be a valid IP host name.
    2276              :   The string can be empty ("") if to connect to the local computer.
    2277              : @param exp_name Specifies the experiment to connect to.
    2278              :   If this string is empty, the number of defined experiments in exptab is checked.
    2279              :   If only one experiment is defined, the function automatically connects to this
    2280              :   one. If more than one experiment is defined, a list is presented and the user
    2281              :   can interactively select one experiment.
    2282              : @param client_name Client name of the calling program as it can be seen by
    2283              :   others (like the scl command in ODBEdit).
    2284              : @param func Callback function to read in a password if security has
    2285              :   been enabled. In all command line applications this function is NULL which
    2286              :   invokes an internal ss_gets() function to read in a password.
    2287              :   In windows environments (MS Windows, X Windows) a function can be supplied to
    2288              :   open a dialog box and read in the password. The argument of this function must
    2289              :   be the returned password.
    2290              : @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br>
    2291              : CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
    2292              : */
    2293            0 : INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func)(char *)) {
    2294              :    INT status;
    2295              : 
    2296            0 :    status = cm_connect_experiment1(host_name, exp_name, client_name, func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
    2297            0 :    cm_msg_flush_buffer();
    2298            0 :    if (status != CM_SUCCESS) {
    2299            0 :       std::string s = cm_get_error(status);
    2300            0 :       puts(s.c_str());
    2301            0 :    }
    2302              : 
    2303            0 :    return status;
    2304              : }
    2305              : 
    2306              : /********************************************************************/
    2307              : /**
    2308              : Connect to a MIDAS experiment (to the online database) on
    2309              :            a specific host.
    2310              : @internal
    2311              : */
    2312            2 : INT cm_connect_experiment1(const char *host_name, const char *default_exp_name,
    2313              :                            const char *client_name, void (*func)(char *), INT odb_size, DWORD watchdog_timeout) {
    2314              :    INT status, size;
    2315              :    char client_name1[NAME_LENGTH];
    2316              :    char password[NAME_LENGTH], str[256];
    2317            2 :    HNDLE hDB = 0, hKeyClient = 0;
    2318              :    BOOL call_watchdog;
    2319              : 
    2320            2 :    ss_tzset(); // required for localtime_r()
    2321              : 
    2322            2 :    if (_hKeyClient)
    2323            0 :       cm_disconnect_experiment();
    2324              : 
    2325            2 :    cm_msg_early_init();
    2326              : 
    2327              :    //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before connecting to experiment");
    2328              :    //cm_msg_flush_buffer();
    2329              : 
    2330            2 :    rpc_set_name(client_name);
    2331              : 
    2332              :    /* check for local host */
    2333            2 :    if (equal_ustring(host_name, "local"))
    2334            0 :       host_name = NULL;
    2335              : 
    2336              : #ifdef OS_WINNT
    2337              :    {
    2338              :       WSADATA WSAData;
    2339              : 
    2340              :       /* Start windows sockets */
    2341              :       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
    2342              :          return RPC_NET_ERROR;
    2343              :    }
    2344              : #endif
    2345              : 
    2346            2 :    std::string default_exp_name1;
    2347            2 :    if (default_exp_name)
    2348            2 :       default_exp_name1 = default_exp_name;
    2349              : 
    2350              :    /* connect to MIDAS server */
    2351            2 :    if (host_name && host_name[0]) {
    2352            0 :       if (default_exp_name1.length() == 0) {
    2353            0 :          status = cm_select_experiment_remote(host_name, &default_exp_name1);
    2354            0 :          if (status != CM_SUCCESS)
    2355            0 :             return status;
    2356              :       }
    2357              : 
    2358            0 :       cm_set_experiment_name(default_exp_name1.c_str());
    2359              : 
    2360            0 :       status = rpc_server_connect(host_name, default_exp_name1.c_str());
    2361            0 :       if (status != RPC_SUCCESS)
    2362            0 :          return status;
    2363              : 
    2364              :       /* register MIDAS library functions */
    2365            0 :       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
    2366            0 :       if (status != RPC_SUCCESS)
    2367            0 :          return status;
    2368              :    } else {
    2369              :       /* lookup path for *SHM files and save it */
    2370              : 
    2371              : #ifdef LOCAL_ROUTINES
    2372            2 :       status = cm_set_experiment_local(default_exp_name1.c_str());
    2373            2 :       if (status != CM_SUCCESS)
    2374            0 :          return status;
    2375              : 
    2376            2 :       default_exp_name1 = cm_get_experiment_name();
    2377              : 
    2378            2 :       ss_suspend_init_odb_port();
    2379              : 
    2380              :       INT semaphore_elog, semaphore_alarm, semaphore_history, semaphore_msg;
    2381              : 
    2382              :       /* create alarm and elog semaphores */
    2383            2 :       status = ss_semaphore_create("ALARM", &semaphore_alarm);
    2384            2 :       if (status != SS_CREATED && status != SS_SUCCESS) {
    2385            0 :          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
    2386            0 :          return status;
    2387              :       }
    2388            2 :       status = ss_semaphore_create("ELOG", &semaphore_elog);
    2389            2 :       if (status != SS_CREATED && status != SS_SUCCESS) {
    2390            0 :          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
    2391            0 :          return status;
    2392              :       }
    2393            2 :       status = ss_semaphore_create("HISTORY", &semaphore_history);
    2394            2 :       if (status != SS_CREATED && status != SS_SUCCESS) {
    2395            0 :          cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
    2396            0 :          return status;
    2397              :       }
    2398            2 :       status = ss_semaphore_create("MSG", &semaphore_msg);
    2399            2 :       if (status != SS_CREATED && status != SS_SUCCESS) {
    2400            0 :          cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
    2401            0 :          return status;
    2402              :       }
    2403              : 
    2404            2 :       cm_set_experiment_semaphore(semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg);
    2405              : #else
    2406              :       return CM_UNDEF_EXP;
    2407              : #endif
    2408              :    }
    2409              : 
    2410              :    //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before open ODB");
    2411              :    //cm_msg_flush_buffer();
    2412              : 
    2413              :    /* open ODB */
    2414            2 :    if (odb_size == 0)
    2415            1 :       odb_size = DEFAULT_ODB_SIZE;
    2416              : 
    2417            2 :    status = db_open_database("ODB", odb_size, &hDB, client_name);
    2418            2 :    if (status != DB_SUCCESS && status != DB_CREATED) {
    2419            0 :       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database, db_open_database() status %d", status);
    2420            0 :       return status;
    2421              :    }
    2422              : 
    2423              :    //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after open ODB");
    2424              :    //cm_msg_flush_buffer();
    2425              : 
    2426            2 :    int odb_timeout = db_set_lock_timeout(hDB, 0);
    2427            2 :    size = sizeof(odb_timeout);
    2428            2 :    status = db_get_value(hDB, 0, "/Experiment/ODB timeout", &odb_timeout, &size, TID_INT32, TRUE);
    2429            2 :    if (status != DB_SUCCESS) {
    2430            0 :       cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/ODB timeout, status %d", status);
    2431              :    }
    2432              : 
    2433            2 :    if (odb_timeout > 0) {
    2434            2 :       db_set_lock_timeout(hDB, odb_timeout);
    2435              :    }
    2436              : 
    2437            2 :    BOOL protect_odb = FALSE;
    2438            2 :    size = sizeof(protect_odb);
    2439            2 :    status = db_get_value(hDB, 0, "/Experiment/Protect ODB", &protect_odb, &size, TID_BOOL, TRUE);
    2440            2 :    if (status != DB_SUCCESS) {
    2441            0 :       cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Protect ODB, status %d", status);
    2442              :    }
    2443              : 
    2444            2 :    if (protect_odb) {
    2445            0 :       db_protect_database(hDB);
    2446              :    }
    2447              : 
    2448            2 :    BOOL enable_core_dumps = FALSE;
    2449            2 :    size = sizeof(enable_core_dumps);
    2450            2 :    status = db_get_value(hDB, 0, "/Experiment/Enable core dumps", &enable_core_dumps, &size, TID_BOOL, TRUE);
    2451            2 :    if (status != DB_SUCCESS) {
    2452            0 :       cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Enable core dumps, status %d", status);
    2453              :    }
    2454              : 
    2455            2 :    if (enable_core_dumps) {
    2456              : #ifdef RLIMIT_CORE
    2457              :       struct rlimit limit;
    2458            0 :       limit.rlim_cur = RLIM_INFINITY;
    2459            0 :       limit.rlim_max = RLIM_INFINITY;
    2460            0 :       status = setrlimit(RLIMIT_CORE, &limit);
    2461            0 :       if (status != 0) {
    2462            0 :          cm_msg(MERROR, "cm_connect_experiment", "Cannot setrlimit(RLIMIT_CORE, RLIM_INFINITY), errno %d (%s)", errno,
    2463            0 :                 strerror(errno));
    2464              :       }
    2465              : #else
    2466              : #warning setrlimit(RLIMIT_CORE) is not available
    2467              : #endif
    2468              :    }
    2469              : 
    2470            2 :    size = sizeof(disable_bind_rpc_to_localhost);
    2471            2 :    status = db_get_value(hDB, 0, "/Experiment/Security/Enable non-localhost RPC", &disable_bind_rpc_to_localhost, &size,
    2472              :                          TID_BOOL, TRUE);
    2473            2 :    if (status != DB_SUCCESS) {
    2474            0 :       cm_msg(MERROR, "cm_connect_experiment1",
    2475              :              "cannot get ODB /Experiment/Security/Enable non-localhost RPC, status %d", status);
    2476              :    }
    2477              : 
    2478            2 :    std::string local_host_name;
    2479              :    
    2480              :    /* now setup client info */
    2481            2 :    if (!disable_bind_rpc_to_localhost)
    2482            2 :       local_host_name = "localhost";
    2483              :    else
    2484            0 :       local_host_name = ss_gethostname();
    2485              : 
    2486              :    /* check watchdog timeout */
    2487            2 :    if (watchdog_timeout == 0)
    2488            0 :       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
    2489              : 
    2490            2 :    strcpy(client_name1, client_name);
    2491            2 :    password[0] = 0;
    2492            2 :    status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
    2493              : 
    2494            2 :    if (status == CM_WRONG_PASSWORD) {
    2495            0 :       if (func == NULL)
    2496            0 :          strcpy(str, ss_getpass("Password: "));
    2497              :       else
    2498            0 :          func(str);
    2499              : 
    2500            0 :       strcpy(password, ss_crypt(str, "mi"));
    2501            0 :       status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
    2502            0 :       if (status != CM_SUCCESS) {
    2503              :          /* disconnect */
    2504            0 :          if (rpc_is_remote())
    2505            0 :             rpc_server_disconnect();
    2506            0 :          cm_disconnect_experiment();
    2507              : 
    2508            0 :          return status;
    2509              :       }
    2510              :    }
    2511              : 
    2512              :    //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set client info");
    2513              :    //cm_msg_flush_buffer();
    2514              : 
    2515              :    /* tell the rest of MIDAS that ODB is open for business */
    2516              : 
    2517            2 :    cm_set_experiment_database(hDB, hKeyClient);
    2518              : 
    2519              :    //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set experiment database");
    2520              :    //cm_msg_flush_buffer();
    2521              : 
    2522              :    /* cm_msg_open_buffer() calls bm_open_buffer() calls ODB function
    2523              :     * to get event buffer size, etc */
    2524              : 
    2525            2 :    status = cm_msg_open_buffer();
    2526            2 :    if (status != CM_SUCCESS) {
    2527            0 :       cm_msg(MERROR, "cm_connect_experiment1", "cannot open message buffer, cm_msg_open_buffer() status %d", status);
    2528            0 :       return status;
    2529              :    }
    2530              : 
    2531              :    //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after message system is ready");
    2532              :    //cm_msg_flush_buffer();
    2533              : 
    2534              :    /* set experiment name in ODB if not present */
    2535            2 :    std::string current_name;
    2536            2 :    db_get_value_string(hDB, 0, "/Experiment/Name", 0, &current_name, TRUE);
    2537            2 :    if (current_name.length() == 0 || current_name == "Default") {
    2538            1 :       db_set_value_string(hDB, 0, "/Experiment/Name", &default_exp_name1);
    2539              :    }
    2540              : 
    2541            2 :    if (!rpc_is_remote()) {
    2542              :       /* experiment path is only set for local connections */
    2543              :       /* set data dir in ODB */
    2544            2 :       std::string path = cm_get_path();
    2545            2 :       db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, TRUE);
    2546            2 :    }
    2547              : 
    2548              :    /* register server to be able to be called by other clients */
    2549            2 :    status = cm_register_server();
    2550            2 :    if (status != CM_SUCCESS) {
    2551            0 :       cm_msg(MERROR, "cm_connect_experiment", "Cannot register RPC server, cm_register_server() status %d", status);
    2552            0 :       if (!equal_ustring(client_name, "odbedit")) {
    2553            0 :          return status;
    2554              :       }
    2555              :    }
    2556              : 
    2557              :    /* set watchdog timeout */
    2558            2 :    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
    2559            2 :    size = sizeof(watchdog_timeout);
    2560            2 :    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
    2561            2 :    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
    2562            2 :    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
    2563              : 
    2564              :    /* set command line */
    2565            2 :    std::string cmdline = ss_get_cmdline();
    2566            2 :    std::string path = "/Programs/" + std::string(client_name);
    2567            2 :    midas::odb prog(path);
    2568            6 :    if (!midas::odb::exists(path + "/Start command") ||
    2569            6 :        prog["Start command"] == std::string(""))
    2570            2 :       prog["Start command"].set_string_size(cmdline, 256);
    2571              : 
    2572              :    /* get final client name */
    2573            2 :    std::string xclient_name = rpc_get_name();
    2574              : 
    2575              :    /* startup message is not displayed */
    2576            2 :    cm_msg(MLOG, "cm_connect_experiment", "Program %s on host %s started", xclient_name.c_str(), local_host_name.c_str());
    2577              : 
    2578              :    /* enable system and user messages to stdout as default */
    2579            2 :    cm_set_msg_print(MT_ALL, MT_ALL, puts);
    2580              : 
    2581              :    /* call cm_check_connect when exiting */
    2582            2 :    atexit((void (*)(void)) cm_check_connect);
    2583              : 
    2584              :    /* register ctrl-c handler */
    2585            2 :    ss_ctrlc_handler(cm_ctrlc_handler);
    2586              : 
    2587              :    //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after connect to experiment is complete");
    2588              :    //cm_msg_flush_buffer();
    2589              : 
    2590            2 :    return CM_SUCCESS;
    2591            2 : }
    2592              : 
    2593              : #ifdef LOCAL_ROUTINES
    2594              : /********************************************************************/
    2595              : /**
    2596              : Read exptab and return all defined experiments in *exp_name[MAX_EXPERIMENTS]
    2597              : @param  host_name         Internet host name.
    2598              : @param  exp_name          list of experiment names
    2599              : @return CM_SUCCESS, RPC_NET_ERROR
    2600              : */
    2601            2 : INT cm_list_experiments_local(STRING_LIST *exp_names) {
    2602            2 :    assert(exp_names != NULL);
    2603            2 :    exp_names->clear();
    2604              : 
    2605            2 :    if (_exptab.exptab.size() == 0) {
    2606            2 :       int status = cm_read_exptab(&_exptab);
    2607            2 :       if (status != CM_SUCCESS)
    2608            0 :          return status;
    2609              :    }
    2610              :    
    2611            4 :    for (unsigned i=0; i<_exptab.exptab.size(); i++) {
    2612            2 :       exp_names->push_back(_exptab.exptab[i].name);
    2613              :    }
    2614              :    
    2615            2 :    return CM_SUCCESS;
    2616              : }
    2617              : #endif // LOCAL_ROUTINES
    2618              : 
    2619              : /********************************************************************/
    2620              : /**
    2621              : Connect to a MIDAS server and return all defined
    2622              :            experiments in *exp_name[MAX_EXPERIMENTS]
    2623              : @param  host_name         Internet host name.
    2624              : @param  exp_name          list of experiment names
    2625              : @return CM_SUCCESS, RPC_NET_ERROR
    2626              : */
    2627            0 : INT cm_list_experiments_remote(const char *host_name, STRING_LIST *exp_names) {
    2628              :    INT status;
    2629              :    INT sock;
    2630            0 :    int port = MIDAS_TCP_PORT;
    2631              :    char hname[256];
    2632              :    char *s;
    2633              : 
    2634            0 :    assert(exp_names != NULL);
    2635            0 :    exp_names->clear();
    2636              : 
    2637              :    /* extract port number from host_name */
    2638            0 :    mstrlcpy(hname, host_name, sizeof(hname));
    2639            0 :    s = strchr(hname, ':');
    2640            0 :    if (s) {
    2641            0 :       *s = 0;
    2642            0 :       port = strtoul(s + 1, NULL, 0);
    2643              :    }
    2644              : 
    2645            0 :    std::string errmsg;
    2646              : 
    2647            0 :    status = ss_socket_connect_tcp(hname, port, &sock, &errmsg);
    2648              : 
    2649            0 :    if (status != SS_SUCCESS) {
    2650            0 :       cm_msg(MERROR, "cm_list_experiments_remote", "Cannot connect to \"%s\" port %d: %s", hname, port, errmsg.c_str());
    2651            0 :       return RPC_NET_ERROR;
    2652              :    }
    2653              : 
    2654              :    /* request experiment list */
    2655            0 :    send(sock, "I", 2, 0);
    2656              : 
    2657              :    while (1) {
    2658              :       char str[256];
    2659              : 
    2660            0 :       status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
    2661              : 
    2662            0 :       if (status < 0)
    2663            0 :          return RPC_NET_ERROR;
    2664              : 
    2665            0 :       if (status == 0)
    2666            0 :          break;
    2667              : 
    2668            0 :       exp_names->push_back(str);
    2669            0 :    }
    2670              : 
    2671            0 :    ss_socket_close(&sock);
    2672              : 
    2673            0 :    return CM_SUCCESS;
    2674            0 : }
    2675              : 
    2676              : #ifdef LOCAL_ROUTINES
    2677              : /********************************************************************/
    2678              : /**
    2679              : Read exptab and select an experiment
    2680              :            from the experiments available on this server
    2681              : @internal
    2682              : @param  exp_name          selected experiment name
    2683              : @return CM_SUCCESS
    2684              : */
    2685            1 : INT cm_select_experiment_local(std::string *exp_name) {
    2686              :    INT status;
    2687            1 :    STRING_LIST expts;
    2688              : 
    2689            1 :    assert(exp_name != NULL);
    2690              : 
    2691              :    /* retrieve list of experiments and make selection */
    2692            1 :    status = cm_list_experiments_local(&expts);
    2693            1 :    if (status != CM_SUCCESS)
    2694            0 :       return status;
    2695              : 
    2696            1 :    if (expts.size() == 1) {
    2697            1 :       *exp_name = expts[0];
    2698            0 :    } else if (expts.size() > 1) {
    2699            0 :       printf("Available experiments on local computer:\n");
    2700              : 
    2701            0 :       for (unsigned i = 0; i < expts.size(); i++) {
    2702            0 :          printf("%d : %s\n", i, expts[i].c_str());
    2703              :       }
    2704              : 
    2705              :       while (1) {
    2706            0 :          printf("Select number from 0 to %d: ", ((int)expts.size())-1);
    2707              :          char str[32];
    2708            0 :          ss_gets(str, 32);
    2709            0 :          int isel = atoi(str);
    2710            0 :          if (isel < 0)
    2711            0 :             continue;
    2712            0 :          if (isel >= (int)expts.size())
    2713            0 :             continue;
    2714            0 :          *exp_name = expts[isel];
    2715            0 :          break;
    2716            0 :       }
    2717              :    } else {
    2718            0 :       return CM_UNDEF_EXP;
    2719              :    }
    2720              : 
    2721            1 :    return CM_SUCCESS;
    2722            1 : }
    2723              : #endif // LOCAL_ROUTINES
    2724              : 
    2725              : /********************************************************************/
    2726              : /**
    2727              : Connect to a MIDAS server and select an experiment
    2728              :            from the experiments available on this server
    2729              : @internal
    2730              : @param  host_name         Internet host name.
    2731              : @param  exp_name          selected experiment name
    2732              : @return CM_SUCCESS, RPC_NET_ERROR
    2733              : */
    2734            0 : INT cm_select_experiment_remote(const char *host_name, std::string *exp_name) {
    2735              :    INT status;
    2736            0 :    STRING_LIST expts;
    2737              : 
    2738            0 :    assert(exp_name != NULL);
    2739              : 
    2740              :    /* retrieve list of experiments and make selection */
    2741            0 :    status = cm_list_experiments_remote(host_name, &expts);
    2742            0 :    if (status != CM_SUCCESS)
    2743            0 :       return status;
    2744              : 
    2745            0 :    if (expts.size() > 1) {
    2746            0 :       printf("Available experiments on server %s:\n", host_name);
    2747              : 
    2748            0 :       for (unsigned i = 0; i < expts.size(); i++) {
    2749            0 :          printf("%d : %s\n", i, expts[i].c_str());
    2750              :       }
    2751              : 
    2752              :       while (1) {
    2753            0 :          printf("Select number from 0 to %d: ", ((int)expts.size())-1);
    2754              :          char str[32];
    2755            0 :          ss_gets(str, 32);
    2756            0 :          int isel = atoi(str);
    2757            0 :          if (isel < 0)
    2758            0 :             continue;
    2759            0 :          if (isel >= (int)expts.size())
    2760            0 :             continue;
    2761            0 :          *exp_name = expts[isel];
    2762            0 :          break;
    2763            0 :       }
    2764              :    } else {
    2765            0 :       *exp_name = expts[0];
    2766              :    }
    2767              : 
    2768            0 :    return CM_SUCCESS;
    2769            0 : }
    2770              : 
    2771              : /********************************************************************/
    2772              : /**
    2773              : Connect to a MIDAS client of the current experiment
    2774              : @internal
    2775              : @param  client_name       Name of client to connect to. This name
    2776              :                             is set by the other client via the
    2777              :                             cm_connect_experiment call.
    2778              : @param  hConn            Connection handle
    2779              : @return CM_SUCCESS, CM_NO_CLIENT
    2780              : */
    2781            0 : INT cm_connect_client(const char *client_name, HNDLE *hConn) {
    2782              :    HNDLE hDB, hKeyRoot, hSubkey, hKey;
    2783              :    INT status, i, length, port;
    2784              :    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
    2785              : 
    2786              :    /* find client entry in ODB */
    2787            0 :    cm_get_experiment_database(&hDB, &hKey);
    2788              : 
    2789            0 :    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
    2790            0 :    if (status != DB_SUCCESS)
    2791            0 :       return status;
    2792              : 
    2793            0 :    i = 0;
    2794              :    do {
    2795              :       /* search for client with specific name */
    2796            0 :       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
    2797            0 :       if (status == DB_NO_MORE_SUBKEYS)
    2798            0 :          return CM_NO_CLIENT;
    2799              : 
    2800            0 :       status = db_find_key(hDB, hSubkey, "Name", &hKey);
    2801            0 :       if (status != DB_SUCCESS)
    2802            0 :          return status;
    2803              : 
    2804            0 :       length = NAME_LENGTH;
    2805            0 :       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
    2806            0 :       if (status != DB_SUCCESS)
    2807            0 :          return status;
    2808              : 
    2809            0 :       if (equal_ustring(name, client_name)) {
    2810            0 :          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
    2811            0 :          if (status != DB_SUCCESS)
    2812            0 :             return status;
    2813              : 
    2814            0 :          length = sizeof(INT);
    2815            0 :          status = db_get_data(hDB, hKey, &port, &length, TID_INT32);
    2816            0 :          if (status != DB_SUCCESS)
    2817            0 :             return status;
    2818              : 
    2819            0 :          status = db_find_key(hDB, hSubkey, "Host", &hKey);
    2820            0 :          if (status != DB_SUCCESS)
    2821            0 :             return status;
    2822              : 
    2823            0 :          length = sizeof(host_name);
    2824            0 :          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
    2825            0 :          if (status != DB_SUCCESS)
    2826            0 :             return status;
    2827              : 
    2828              :          /* client found -> connect to its server port */
    2829            0 :          return rpc_client_connect(host_name, port, client_name, hConn);
    2830              :       }
    2831              : 
    2832              : 
    2833              :    } while (TRUE);
    2834              : }
    2835              : 
    2836              : static void rpc_client_shutdown();
    2837              : 
    2838              : /********************************************************************/
    2839              : /**
    2840              : Disconnect from a MIDAS client
    2841              : @param   hConn             Connection handle obtained via
    2842              :                              cm_connect_client()
    2843              : @param   bShutdown         If TRUE, disconnect from client and
    2844              :                              shut it down (exit the client program)
    2845              :                              by sending a RPC_SHUTDOWN message
    2846              : @return   see rpc_client_disconnect()
    2847              : */
    2848            0 : INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown) {
    2849            0 :    return rpc_client_disconnect(hConn, bShutdown);
    2850              : }
    2851              : 
    2852              : /********************************************************************/
    2853              : /**
    2854              : Disconnect from a MIDAS experiment.
    2855              : @attention Should be the last call to a MIDAS library function in an
    2856              : application before it exits. This function removes the client information
    2857              : from the ODB, disconnects all TCP connections and frees all internal
    2858              : allocated memory. See cm_connect_experiment() for example.
    2859              : @return CM_SUCCESS
    2860              : */
    2861            2 : INT cm_disconnect_experiment(void) {
    2862              :    HNDLE hDB, hKey;
    2863              : 
    2864              :    //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before disconnect from experiment");
    2865              :    //cm_msg_flush_buffer();
    2866              : 
    2867              :    /* wait on any transition thread */
    2868            2 :    if (_trp.transition && !_trp.finished) {
    2869            0 :       printf("Waiting for transition to finish...\n");
    2870              :       do {
    2871            0 :          ss_sleep(10);
    2872            0 :       } while (!_trp.finished);
    2873              :    }
    2874              : 
    2875              :    /* stop the watchdog thread */
    2876            2 :    cm_stop_watchdog_thread();
    2877              : 
    2878              :    /* send shutdown notification */
    2879            2 :    std::string client_name = rpc_get_name();
    2880              : 
    2881            2 :    std::string local_host_name;
    2882              :    
    2883            2 :    if (!disable_bind_rpc_to_localhost)
    2884            2 :       local_host_name = "localhost";
    2885              :    else {
    2886            0 :       local_host_name = ss_gethostname();
    2887              :       //if (strchr(local_host_name, '.'))
    2888              :       //   *strchr(local_host_name, '.') = 0;
    2889              :    }
    2890              : 
    2891              :    /* disconnect message not displayed */
    2892            2 :    cm_msg(MLOG, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name.c_str(), local_host_name.c_str());
    2893            2 :    cm_msg_flush_buffer();
    2894              : 
    2895            2 :    if (rpc_is_remote()) {
    2896            0 :       if (rpc_is_connected()) {
    2897              :          /* close open records */
    2898            0 :          db_close_all_records();
    2899              : 
    2900            0 :          cm_msg_close_buffer();
    2901              :       }
    2902              : 
    2903            0 :       rpc_client_shutdown();
    2904            0 :       rpc_server_disconnect();
    2905              : 
    2906            0 :       cm_set_experiment_database(0, 0);
    2907              :    } else {
    2908            2 :       rpc_client_shutdown();
    2909              : 
    2910              :       /* delete client info */
    2911            2 :       cm_get_experiment_database(&hDB, &hKey);
    2912              : 
    2913            2 :       if (hDB)
    2914            2 :          cm_delete_client_info(hDB, 0);
    2915              : 
    2916              :       //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before close all buffers, close all databases");
    2917              :       //cm_msg_flush_buffer();
    2918              : 
    2919            2 :       cm_msg_close_buffer();
    2920            2 :       bm_close_all_buffers();
    2921            2 :       db_close_all_databases();
    2922              : 
    2923            2 :       cm_set_experiment_database(0, 0);
    2924              : 
    2925              :       //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after close all buffers, close all databases");
    2926              :       //cm_msg_flush_buffer();
    2927              :    }
    2928              : 
    2929            2 :    if (!rpc_is_mserver())
    2930            2 :       rpc_server_shutdown();
    2931              : 
    2932              :    /* free RPC list */
    2933            2 :    rpc_deregister_functions();
    2934              : 
    2935              :    //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before deleting the message ring buffer");
    2936              :    //cm_msg_flush_buffer();
    2937              : 
    2938              :    /* last flush before we delete the message ring buffer */
    2939            2 :    cm_msg_flush_buffer();
    2940              : 
    2941              :    //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after disconnect is completed");
    2942              :    //cm_msg_flush_buffer();
    2943              : 
    2944            2 :    return CM_SUCCESS;
    2945            2 : }
    2946              : 
    2947              : /********************************************************************/
    2948              : /**
    2949              : Set the handle to the ODB for the currently connected experiment
    2950              : @param hDB              Database handle
    2951              : @param hKeyClient       Key handle of client structure
    2952              : @return CM_SUCCESS
    2953              : */
    2954            6 : INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient) {
    2955              :    //printf("cm_set_experiment_database: hDB %d, hKeyClient %d\n", hDB, hKeyClient);
    2956              : 
    2957            6 :    _hDB = hDB;
    2958            6 :    _hKeyClient = hKeyClient;
    2959              : 
    2960              :    //if (hDB == 0) {
    2961              :    //   rpc_set_server_option(RPC_ODB_HANDLE, 0);
    2962              :    //}
    2963              : 
    2964            6 :    return CM_SUCCESS;
    2965              : }
    2966              : 
    2967              : 
    2968              : 
    2969              : /**dox***************************************************************/
    2970              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    2971              : 
    2972              : /********************************************************************/
    2973            2 : INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
    2974              : /********************************************************************\
    2975              : 
    2976              :   Routine: cm_set_experiment_semaphore
    2977              : 
    2978              :   Purpose: Set the handle to the experiment wide semaphorees
    2979              : 
    2980              :   Input:
    2981              :     INT    semaphore_alarm      Alarm semaphore
    2982              :     INT    semaphore_elog       Elog semaphore
    2983              :     INT    semaphore_history    History semaphore
    2984              :     INT    semaphore_msg        Message semaphore
    2985              : 
    2986              :   Output:
    2987              :     none
    2988              : 
    2989              :   Function value:
    2990              :     CM_SUCCESS              Successful completion
    2991              : 
    2992              : \********************************************************************/
    2993              : {
    2994            2 :    _semaphore_alarm = semaphore_alarm;
    2995            2 :    _semaphore_elog = semaphore_elog;
    2996            2 :    _semaphore_history = semaphore_history;
    2997              :    //_semaphore_msg = semaphore_msg;
    2998              : 
    2999            2 :    return CM_SUCCESS;
    3000              : }
    3001              : 
    3002              : /**dox***************************************************************/
    3003              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    3004              : 
    3005              : /********************************************************************/
    3006              : /**
    3007              : Get the handle to the ODB from the currently connected experiment.
    3008              : 
    3009              : @attention This function returns the handle of the online database (ODB) which
    3010              : can be used in future db_xxx() calls. The hkeyclient key handle can be used
    3011              : to access the client information in the ODB. If the client key handle is not needed,
    3012              : the parameter can be NULL.
    3013              : \code
    3014              : HNDLE hDB, hkeyclient;
    3015              :  char  name[32];
    3016              :  int   size;
    3017              :  db_get_experiment_database(&hdb, &hkeyclient);
    3018              :  size = sizeof(name);
    3019              :  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
    3020              :  printf("My name is %s\n", name);
    3021              : \endcode
    3022              : @param hDB Database handle.
    3023              : @param hKeyClient Handle for key where search starts, zero for root.
    3024              : @return CM_SUCCESS
    3025              : */
    3026           36 : INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient) {
    3027           36 :    if (_hDB) {
    3028              :       //printf("cm_get_experiment_database %d %d\n", _hDB, _hKeyClient);
    3029           36 :       if (hDB != NULL)
    3030           36 :          *hDB = _hDB;
    3031           36 :       if (hKeyClient != NULL)
    3032           13 :          *hKeyClient = _hKeyClient;
    3033           36 :       return CM_SUCCESS;
    3034              :    } else {
    3035              :       //printf("cm_get_experiment_database no init\n");
    3036            0 :       if (hDB != NULL)
    3037            0 :          *hDB = 0;
    3038            0 :       if (hKeyClient != NULL)
    3039            0 :          *hKeyClient = 0;
    3040            0 :       return CM_DB_ERROR;
    3041              :    }
    3042              : }
    3043              : 
    3044              : /**dox***************************************************************/
    3045              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    3046              : 
    3047              : /********************************************************************/
    3048            0 : INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
    3049              : /********************************************************************\
    3050              : 
    3051              :   Routine: cm_get_experiment_semaphore
    3052              : 
    3053              :   Purpose: Get the handle to the experiment wide semaphores
    3054              : 
    3055              :   Input:
    3056              :     none
    3057              : 
    3058              :   Output:
    3059              :     INT    semaphore_alarm      Alarm semaphore
    3060              :     INT    semaphore_elog       Elog semaphore
    3061              :     INT    semaphore_history    History semaphore
    3062              :     INT    semaphore_msg        Message semaphore
    3063              : 
    3064              :   Function value:
    3065              :     CM_SUCCESS              Successful completion
    3066              : 
    3067              : \********************************************************************/
    3068              : {
    3069            0 :    if (semaphore_alarm)
    3070            0 :       *semaphore_alarm = _semaphore_alarm;
    3071            0 :    if (semaphore_elog)
    3072            0 :       *semaphore_elog = _semaphore_elog;
    3073            0 :    if (semaphore_history)
    3074            0 :       *semaphore_history = _semaphore_history;
    3075              :    //if (semaphore_msg)
    3076              :    //   *semaphore_msg = _semaphore_msg;
    3077            0 :    if (semaphore_msg)
    3078            0 :       *semaphore_msg = -1;
    3079              : 
    3080            0 :    return CM_SUCCESS;
    3081              : }
    3082              : 
    3083              : /**dox***************************************************************/
    3084              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    3085              : 
    3086              : #ifdef LOCAL_ROUTINES
    3087              : static BUFFER* bm_get_buffer(const char *who, INT buffer_handle, int *pstatus);
    3088              : static int bm_lock_buffer_read_cache(BUFFER *pbuf);
    3089              : static int bm_lock_buffer_write_cache(BUFFER *pbuf);
    3090              : static int bm_lock_buffer_mutex(BUFFER *pbuf);
    3091              : static int xbm_lock_buffer(BUFFER *pbuf);
    3092              : static void xbm_unlock_buffer(BUFFER *pbuf);
    3093              : 
    3094              : class bm_lock_buffer_guard
    3095              : {
    3096              : public:
    3097              :    bool fDebug = false;
    3098              :    
    3099              : public:
    3100           25 :    bm_lock_buffer_guard(BUFFER* pbuf, bool do_not_lock=false) // ctor
    3101           25 :    {
    3102           25 :       assert(pbuf != NULL);
    3103           25 :       fBuf = pbuf;
    3104           25 :       if (do_not_lock) {
    3105            0 :          if (fDebug)
    3106            0 :             printf("lock_buffer_guard(%s) ctor without lock\n", fBuf->buffer_name);
    3107            0 :          return;
    3108              :       }
    3109           25 :       if (fDebug)
    3110            0 :          printf("lock_buffer_guard(%s) ctor\n", fBuf->buffer_name);
    3111           25 :       int status = xbm_lock_buffer(fBuf);
    3112           25 :       if (status != BM_SUCCESS) {
    3113            0 :          fLocked = false;
    3114            0 :          fError  = true;
    3115            0 :          fStatus = status;
    3116              :       } else {
    3117           25 :          fLocked = true;
    3118              :       }
    3119              :    }
    3120              : 
    3121           25 :    ~bm_lock_buffer_guard() // dtor
    3122              :    {
    3123           25 :       if (fInvalid) {
    3124            0 :          if (fDebug)
    3125            0 :             printf("lock_buffer_guard(invalid) dtor\n");
    3126              :       } else {
    3127           25 :          assert(fBuf != NULL);
    3128           25 :          if (fDebug)
    3129            0 :             printf("lock_buffer_guard(%s) dtor, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
    3130           25 :          if (fLocked) {
    3131           17 :             xbm_unlock_buffer(fBuf);
    3132           17 :             fLocked = false;
    3133           17 :             fError = false;
    3134              :          }
    3135           25 :          fBuf = NULL;
    3136              :       }
    3137           25 :    }
    3138              : 
    3139              :    // make object uncopyable
    3140              :    bm_lock_buffer_guard(const bm_lock_buffer_guard&) = delete;
    3141              :    bm_lock_buffer_guard& operator=(const bm_lock_buffer_guard&) = delete;
    3142              : 
    3143            8 :    void unlock()
    3144              :    {
    3145            8 :       assert(fBuf != NULL);
    3146            8 :       if (fDebug)
    3147            0 :          printf("lock_buffer_guard(%s) unlock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
    3148            8 :       assert(fLocked);
    3149            8 :       xbm_unlock_buffer(fBuf);
    3150            8 :       fLocked = false;
    3151            8 :       fError = false;
    3152            8 :    }
    3153              : 
    3154            0 :    bool relock()
    3155              :    {
    3156            0 :       assert(fBuf != NULL);
    3157            0 :       if (fDebug)
    3158            0 :          printf("lock_buffer_guard(%s) relock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
    3159            0 :       assert(!fLocked);
    3160            0 :       int status = xbm_lock_buffer(fBuf);
    3161            0 :       if (status != BM_SUCCESS) {
    3162            0 :          fLocked = false;
    3163            0 :          fError  = true;
    3164            0 :          fStatus = status;
    3165              :       } else {
    3166            0 :          fLocked = true;
    3167              :       }
    3168            0 :       return fLocked;
    3169              :    }
    3170              : 
    3171            0 :    void invalidate()
    3172              :    {
    3173            0 :       assert(fBuf != NULL);
    3174            0 :       if (fDebug)
    3175            0 :          printf("lock_buffer_guard(%s) invalidate, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
    3176            0 :       assert(!fLocked);
    3177            0 :       fInvalid = true;
    3178            0 :       fBuf = NULL;
    3179            0 :    }
    3180              : 
    3181           25 :    bool is_locked() const
    3182              :    {
    3183           25 :       return fLocked;
    3184              :    }
    3185              : 
    3186              :    bool is_error() const
    3187              :    {
    3188              :       return fError;
    3189              :    }
    3190              : 
    3191            0 :    int get_status() const
    3192              :    {
    3193            0 :       return fStatus;
    3194              :    }
    3195              : 
    3196           23 :    BUFFER* get_pbuf() const
    3197              :    {
    3198           23 :       assert(!fInvalid); // pbuf was deleted
    3199           23 :       assert(fBuf); // we do not return NULL
    3200           23 :       return fBuf;
    3201              :    }
    3202              :    
    3203              : private:
    3204              :    BUFFER* fBuf    = NULL;
    3205              :    bool    fLocked = false;
    3206              :    bool    fError  = false;
    3207              :    bool    fInvalid = false;
    3208              :    int     fStatus = 0;
    3209              : };
    3210              : 
    3211              : static BUFFER_CLIENT *bm_get_my_client_locked(bm_lock_buffer_guard& pbuf_guard);
    3212              : 
    3213              : #endif
    3214              : 
    3215              : static INT bm_notify_client(const char *buffer_name, int s);
    3216              : 
    3217              : static INT bm_push_event(const char *buffer_name);
    3218              : 
    3219              : static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
    3220              :                                 EVENT_HEADER *pevent, void *pdata,
    3221              :                                 EVENT_HANDLER *dispatcher);
    3222              : 
    3223              : /********************************************************************/
    3224              : /**
    3225              : Sets the internal watchdog flags and the own timeout.
    3226              : If call_watchdog is TRUE, the cm_watchdog routine is called
    3227              : periodically from the system to show other clients that
    3228              : this application is "alive". On UNIX systems, the
    3229              : alarm() timer is used which is then not available for
    3230              : user purposes.
    3231              : 
    3232              : The timeout specifies the time, after which the calling
    3233              : application should be considered "dead" by other clients.
    3234              : Normally, the cm_watchdog() routines is called periodically.
    3235              : If a client crashes, this does not occur any more. Then
    3236              : other clients can detect this and clear all buffer and
    3237              : database entries of this application so they are not
    3238              : blocked any more. If this application should not checked
    3239              : by others, the timeout can be specified as zero.
    3240              : It might be useful for debugging purposes to do so,
    3241              : because if a debugger comes to a breakpoint and stops
    3242              : the application, the periodic call of cm_watchdog
    3243              : is disabled and the client looks like dead.
    3244              : 
    3245              : If the timeout is not zero, but the watchdog is not
    3246              : called (call_watchdog == FALSE), the user must ensure
    3247              : to call cm_watchdog periodically with a period of
    3248              : WATCHDOG_INTERVAL milliseconds or less.
    3249              : 
    3250              : An application which calles system routines which block
    3251              : the alarm signal for some time, might increase the
    3252              : timeout to the maximum expected blocking time before
    3253              : issuing the calls. One example is the logger doing
    3254              : Exabyte tape IO, which can take up to one minute.
    3255              : @param    call_watchdog   Call the cm_watchdog routine periodically
    3256              : @param    timeout         Timeout for this application in ms
    3257              : @return   CM_SUCCESS
    3258              : */
    3259            4 : INT cm_set_watchdog_params_local(BOOL call_watchdog, DWORD timeout)
    3260              : {
    3261              : #ifdef LOCAL_ROUTINES
    3262            4 :    _watchdog_timeout = timeout;
    3263              : 
    3264            4 :    std::vector<BUFFER*> mybuffers;
    3265              : 
    3266            4 :    gBuffersMutex.lock();
    3267            4 :    mybuffers = gBuffers;
    3268            4 :    gBuffersMutex.unlock();
    3269              : 
    3270              :    /* set watchdog timeout of all open buffers */
    3271            6 :    for (BUFFER* pbuf : mybuffers) {
    3272              :       
    3273            2 :       if (!pbuf || !pbuf->attached)
    3274            0 :          continue;
    3275              : 
    3276            2 :       bm_lock_buffer_guard pbuf_guard(pbuf);
    3277              : 
    3278            2 :       if (!pbuf_guard.is_locked())
    3279            0 :          continue;
    3280              :       
    3281            2 :       BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
    3282              :       
    3283              :       /* clear entry from client structure in buffer header */
    3284            2 :       pclient->watchdog_timeout = timeout;
    3285              :       
    3286              :       /* show activity */
    3287            2 :       pclient->last_activity = ss_millitime();
    3288            2 :    }
    3289              : 
    3290              :    /* set watchdog timeout for ODB */
    3291            4 :    db_set_watchdog_params(timeout);
    3292              : 
    3293              : #endif /* LOCAL_ROUTINES */
    3294              : 
    3295            4 :    return CM_SUCCESS;
    3296            4 : }
    3297              : 
    3298            4 : INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
    3299              : {
    3300              :    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
    3301            4 :    _watchdog_timeout = timeout;
    3302              : 
    3303            4 :    if (rpc_is_remote()) { // we are connected remotely
    3304              : 
    3305            0 :       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
    3306              : 
    3307            4 :    } else if (rpc_is_mserver()) { // we are the mserver
    3308              : 
    3309            0 :       RPC_SERVER_ACCEPTION* sa = rpc_get_mserver_acception();
    3310            0 :       if (sa)
    3311            0 :          sa->watchdog_timeout = timeout;
    3312              :       
    3313              :       /* write timeout value to client entry in ODB */
    3314              :       HNDLE hDB, hKey;
    3315            0 :       cm_get_experiment_database(&hDB, &hKey);
    3316              :       
    3317            0 :       if (hDB) {
    3318            0 :          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
    3319            0 :          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
    3320            0 :          db_set_mode(hDB, hKey, MODE_READ, TRUE);
    3321              :       }
    3322              : 
    3323              :       /* set the watchdog for the local mserver program */
    3324            0 :       return cm_set_watchdog_params_local(call_watchdog, timeout);
    3325              : 
    3326              :    } else { // only running locally
    3327              : 
    3328            4 :       return cm_set_watchdog_params_local(call_watchdog, timeout);
    3329              : 
    3330              :    }
    3331              : }
    3332              : 
    3333              : /********************************************************************/
    3334              : /**
    3335              : Return the current watchdog parameters
    3336              : @param call_watchdog   Call the cm_watchdog routine periodically
    3337              : @param timeout         Timeout for this application in seconds
    3338              : @return   CM_SUCCESS
    3339              : */
    3340            9 : INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout) {
    3341            9 :    if (call_watchdog)
    3342            7 :       *call_watchdog = FALSE;
    3343            9 :    if (timeout)
    3344            7 :       *timeout = _watchdog_timeout;
    3345              : 
    3346            9 :    return CM_SUCCESS;
    3347              : }
    3348              : 
    3349              : /********************************************************************/
    3350              : /**
    3351              : Return watchdog information about specific client
    3352              : @param    hDB              ODB handle
    3353              : @param    client_name     ODB client name
    3354              : @param    timeout         Timeout for this application in seconds
    3355              : @param    last            Last time watchdog was called in msec
    3356              : @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE
    3357              : */
    3358              : 
    3359            0 : INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
    3360            0 :    if (rpc_is_remote())
    3361            0 :       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
    3362              : 
    3363              : #ifdef LOCAL_ROUTINES
    3364            0 :    return db_get_watchdog_info(hDB, client_name, timeout, last);
    3365              : #else                           /* LOCAL_ROUTINES */
    3366              :    return CM_SUCCESS;
    3367              : #endif                          /* LOCAL_ROUTINES */
    3368              : }
    3369              : 
    3370              : 
    3371              : /**dox***************************************************************/
    3372              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    3373              : 
    3374              : /********************************************************************/
    3375              : 
    3376            2 : static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
    3377              :    int status;
    3378              :    int i, last;
    3379              :    KEY key;
    3380              :    int max_size;
    3381              :    char *str;
    3382              : 
    3383              : //   if (index != -99)
    3384              : //      cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
    3385              : 
    3386            2 :    status = db_get_key(hDB, hKey, &key);
    3387              : 
    3388            2 :    if (status != DB_SUCCESS)
    3389            0 :       return;
    3390              : 
    3391              :    //printf("clear rpc hosts!\n");
    3392            2 :    rpc_clear_allowed_hosts();
    3393              : 
    3394            2 :    max_size = key.item_size;
    3395            2 :    str = (char *) malloc(max_size);
    3396              : 
    3397            2 :    last = 0;
    3398           13 :    for (i = 0; i < key.num_values; i++) {
    3399           11 :       int size = max_size;
    3400           11 :       status = db_get_data_index(hDB, hKey, str, &size, i, TID_STRING);
    3401           11 :       if (status != DB_SUCCESS)
    3402            0 :          break;
    3403              : 
    3404           11 :       if (strlen(str) < 1) // skip emties
    3405            9 :          continue;
    3406              : 
    3407            2 :       if (str[0] == '#') // skip commented-out entries
    3408            0 :          continue;
    3409              : 
    3410              :       //printf("add rpc hosts %d [%s]\n", i, str);
    3411            2 :       rpc_add_allowed_host(str);
    3412            2 :       last = i;
    3413              :    }
    3414              : 
    3415            2 :    if (key.num_values - last < 10) {
    3416            1 :       int new_size = last + 10;
    3417            1 :       status = db_set_num_values(hDB, hKey, new_size);
    3418            1 :       if (status != DB_SUCCESS) {
    3419            0 :          cm_msg(MERROR, "load_rpc_hosts",
    3420              :                 "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
    3421              :       }
    3422              :    }
    3423              : 
    3424            2 :    free(str);
    3425              : }
    3426              : 
    3427            2 : static void init_rpc_hosts(HNDLE hDB) {
    3428              :    int status;
    3429              :    char buf[256];
    3430              :    int size, i;
    3431              :    HNDLE hKey;
    3432              : 
    3433            2 :    strcpy(buf, "localhost");
    3434            2 :    size = sizeof(buf);
    3435              : 
    3436            2 :    status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
    3437              : 
    3438            2 :    if (status != DB_SUCCESS) {
    3439            0 :       cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
    3440              :              status);
    3441            0 :       return;
    3442              :    }
    3443              : 
    3444            2 :    size = sizeof(i);
    3445            2 :    i = 0;
    3446            2 :    status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
    3447              : 
    3448            2 :    if (status != DB_SUCCESS) {
    3449            0 :       cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
    3450            0 :       return;
    3451              :    }
    3452              : 
    3453            2 :    if (i != 0) // RPC hosts check is disabled
    3454            0 :       return;
    3455              : 
    3456            2 :    status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
    3457              : 
    3458            2 :    if (status != DB_SUCCESS || hKey == 0) {
    3459            0 :       cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
    3460              :              status);
    3461            0 :       return;
    3462              :    }
    3463              : 
    3464            2 :    load_rpc_hosts(hDB, hKey, -99, NULL);
    3465              : 
    3466            2 :    status = db_watch(hDB, hKey, load_rpc_hosts, NULL);
    3467              : 
    3468            2 :    if (status != DB_SUCCESS) {
    3469            0 :       cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
    3470            0 :       return;
    3471              :    }
    3472              : }
    3473              : 
    3474              : /********************************************************************/
    3475            2 : INT cm_register_server(void)
    3476              : /********************************************************************\
    3477              : 
    3478              :   Routine: cm_register_server
    3479              : 
    3480              :   Purpose: Register a server which can be called from other clients
    3481              :            of a specific experiment.
    3482              : 
    3483              :   Input:
    3484              :     none
    3485              : 
    3486              :   Output:
    3487              :     none
    3488              : 
    3489              :   Function value:
    3490              :     CM_SUCCESS              Successful completion
    3491              : 
    3492              : \********************************************************************/
    3493              : {
    3494            2 :    if (!_rpc_registered) {
    3495              :       INT status;
    3496              :       int size;
    3497              :       HNDLE hDB, hKey;
    3498              :       char name[NAME_LENGTH];
    3499              :       char str[256];
    3500            2 :       int port = 0;
    3501              : 
    3502            2 :       cm_get_experiment_database(&hDB, &hKey);
    3503              : 
    3504            2 :       size = sizeof(name);
    3505            2 :       status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
    3506              : 
    3507            2 :       if (status != DB_SUCCESS) {
    3508            0 :          cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
    3509            0 :          return status;
    3510              :       }
    3511              : 
    3512            2 :       mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
    3513            2 :       mstrlcat(str, name, sizeof(str));
    3514              : 
    3515            2 :       size = sizeof(port);
    3516            2 :       status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
    3517              : 
    3518            2 :       if (status != DB_SUCCESS) {
    3519            0 :          cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
    3520            0 :          return status;
    3521              :       }
    3522              : 
    3523            2 :       int lport = 0; // actual port number assigned to us by the OS
    3524              : 
    3525            2 :       status = rpc_register_server(port, &_rpc_listen_socket, &lport);
    3526            2 :       if (status != RPC_SUCCESS) {
    3527            0 :          cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
    3528            0 :          return status;
    3529              :       }
    3530              : 
    3531            2 :       _rpc_registered = TRUE;
    3532              : 
    3533              :       /* register MIDAS library functions */
    3534            2 :       rpc_register_functions(rpc_get_internal_list(1), NULL);
    3535              : 
    3536              :       /* store port number in ODB */
    3537              : 
    3538            2 :       status = db_find_key(hDB, hKey, "Server Port", &hKey);
    3539            2 :       if (status != DB_SUCCESS) {
    3540            0 :          cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
    3541            0 :          return status;
    3542              :       }
    3543              : 
    3544              :       /* unlock database */
    3545            2 :       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
    3546              : 
    3547              :       /* set value */
    3548            2 :       status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
    3549            2 :       if (status != DB_SUCCESS) {
    3550            0 :          cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
    3551            0 :          return status;
    3552              :       }
    3553              : 
    3554              :       /* lock database */
    3555            2 :       db_set_mode(hDB, hKey, MODE_READ, TRUE);
    3556              : 
    3557            2 :       init_rpc_hosts(hDB);
    3558              :    }
    3559              : 
    3560            2 :    return CM_SUCCESS;
    3561              : }
    3562              : 
    3563              : /**dox***************************************************************/
    3564              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    3565              : 
    3566              : /********************************************************************/
    3567              : /**
    3568              : Registers a callback function for run transitions.
    3569              : This function internally registers the transition callback
    3570              : function and publishes its request for transition notification by writing
    3571              : a transition request to /System/Clients/\<pid\>/Transition XXX.
    3572              : Other clients making a transition scan the transition requests of all clients
    3573              : and call their transition callbacks via RPC.
    3574              : 
    3575              : Clients can register for transitions (Start/Stop/Pause/Resume) in a given
    3576              : sequence. All sequence numbers given in the registration are sorted on
    3577              : a transition and the clients are contacted in ascending order. By default,
    3578              : all programs register with a sequence number of 500. The logger however
    3579              : uses 200 for start, so that it can open files before the other clients
    3580              : are contacted, and 800 for stop, so that the files get closed when all
    3581              : other clients have gone already through the stop trantition.
    3582              : 
    3583              : The callback function returns CM_SUCCESS if it can perform the transition or
    3584              : a value larger than one in case of error. An error string can be copied
    3585              : into the error variable.
    3586              : @attention The callback function will be called on transitions from inside the
    3587              :     cm_yield() function which therefore must be contained in the main program loop.
    3588              : \code
    3589              : INT start(INT run_number, char *error)
    3590              : {
    3591              :   if (<not ok>)
    3592              :     {
    3593              :     strcpy(error, "Cannot start because ...");
    3594              :     return 2;
    3595              :     }
    3596              :   printf("Starting run %d\n", run_number);
    3597              :   return CM_SUCCESS;
    3598              : }
    3599              : main()
    3600              : {
    3601              :   ...
    3602              :   cm_register_transition(TR_START, start, 500);
    3603              :   do
    3604              :     {
    3605              :     status = cm_yield(1000);
    3606              :     } while (status != RPC_SHUTDOWN &&
    3607              :              status != SS_ABORT);
    3608              :   ...
    3609              : }
    3610              : \endcode
    3611              : @param transition Transition to register for (see @ref state_transition)
    3612              : @param func Callback function.
    3613              : @param sequence_number Sequence number for that transition (1..1000)
    3614              : @return CM_SUCCESS
    3615              : */
    3616            5 : INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
    3617              :    INT status;
    3618              :    HNDLE hDB, hKey, hKeyTrans;
    3619              :    KEY key;
    3620              :    char str[256];
    3621              : 
    3622              :    /* check for valid transition */
    3623            5 :    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME && transition != TR_STARTABORT) {
    3624            0 :       cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
    3625            0 :       return CM_INVALID_TRANSITION;
    3626              :    }
    3627              : 
    3628            5 :    cm_get_experiment_database(&hDB, &hKey);
    3629              : 
    3630            5 :    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
    3631              : 
    3632              :    /* register new transition request */
    3633              : 
    3634              :    {
    3635            5 :       std::lock_guard<std::mutex> guard(_trans_table_mutex);
    3636              : 
    3637           15 :       for (size_t i = 0; i < _trans_table.size(); i++) {
    3638           10 :          if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
    3639            0 :             cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
    3640            0 :             return CM_INVALID_TRANSITION;
    3641              :          }
    3642              :       }
    3643              : 
    3644            5 :       bool found = false;
    3645           15 :       for (size_t i = 0; i < _trans_table.size(); i++) {
    3646           10 :          if (!_trans_table[i].transition) {
    3647            0 :             _trans_table[i].transition = transition;
    3648            0 :             _trans_table[i].sequence_number = sequence_number;
    3649            0 :             _trans_table[i].func = func;
    3650            0 :             found = true;
    3651            0 :             break;
    3652              :          }
    3653              :       }
    3654              : 
    3655            5 :       if (!found) {
    3656              :          TRANS_TABLE tt;
    3657            5 :          tt.transition = transition;
    3658            5 :          tt.sequence_number = sequence_number;
    3659            5 :          tt.func = func;
    3660            5 :          _trans_table.push_back(tt);
    3661              :       }
    3662              : 
    3663              :       // implicit unlock
    3664            5 :    }
    3665              : 
    3666            5 :    sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
    3667              : 
    3668              :    /* unlock database */
    3669            5 :    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
    3670              : 
    3671              :    /* set value */
    3672            5 :    status = db_find_key(hDB, hKey, str, &hKeyTrans);
    3673            5 :    if (!hKeyTrans) {
    3674            5 :       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
    3675            5 :       if (status != DB_SUCCESS)
    3676            0 :          return status;
    3677              :    } else {
    3678            0 :       status = db_get_key(hDB, hKeyTrans, &key);
    3679            0 :       if (status != DB_SUCCESS)
    3680            0 :          return status;
    3681            0 :       status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
    3682            0 :       if (status != DB_SUCCESS)
    3683            0 :          return status;
    3684              :    }
    3685              : 
    3686              :    /* re-lock database */
    3687            5 :    db_set_mode(hDB, hKey, MODE_READ, TRUE);
    3688              : 
    3689            5 :    return CM_SUCCESS;
    3690              : }
    3691              : 
    3692            0 : INT cm_deregister_transition(INT transition) {
    3693              :    INT status;
    3694              :    HNDLE hDB, hKey, hKeyTrans;
    3695              :    char str[256];
    3696              : 
    3697              :    /* check for valid transition */
    3698            0 :    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME && transition != TR_STARTABORT) {
    3699            0 :       cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
    3700            0 :       return CM_INVALID_TRANSITION;
    3701              :    }
    3702              : 
    3703            0 :    cm_get_experiment_database(&hDB, &hKey);
    3704              : 
    3705              :    {
    3706            0 :       std::lock_guard<std::mutex> guard(_trans_table_mutex);
    3707              : 
    3708              :       /* remove existing transition request */
    3709            0 :       for (size_t i = 0; i < _trans_table.size(); i++) {
    3710            0 :          if (_trans_table[i].transition == transition) {
    3711            0 :             _trans_table[i].transition = 0;
    3712            0 :             _trans_table[i].sequence_number = 0;
    3713            0 :             _trans_table[i].func = NULL;
    3714              :          }
    3715              :       }
    3716              : 
    3717              :       // implicit unlock
    3718            0 :    }
    3719              :       
    3720            0 :    sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
    3721              : 
    3722              :    /* unlock database */
    3723            0 :    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
    3724              : 
    3725              :    /* set value */
    3726            0 :    status = db_find_key(hDB, hKey, str, &hKeyTrans);
    3727            0 :    if (hKeyTrans) {
    3728            0 :       status = db_delete_key(hDB, hKeyTrans, FALSE);
    3729            0 :       if (status != DB_SUCCESS)
    3730            0 :          return status;
    3731              :    }
    3732              : 
    3733              :    /* re-lock database */
    3734            0 :    db_set_mode(hDB, hKey, MODE_READ, TRUE);
    3735              : 
    3736            0 :    return CM_SUCCESS;
    3737              : }
    3738              : 
    3739              : /********************************************************************/
    3740              : /**
    3741              : Change the transition sequence for the calling program.
    3742              : @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
    3743              : @param sequence_number New sequence number, should be between 1 and 1000
    3744              : @return     CM_SUCCESS
    3745              : */
    3746            0 : INT cm_set_transition_sequence(INT transition, INT sequence_number) {
    3747              :    INT status;
    3748              :    HNDLE hDB, hKey;
    3749              :    char str[256];
    3750              : 
    3751              :    /* check for valid transition */
    3752            0 :    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
    3753            0 :       cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
    3754            0 :       return CM_INVALID_TRANSITION;
    3755              :    }
    3756              : 
    3757              :    {
    3758            0 :       std::lock_guard<std::mutex> guard(_trans_table_mutex);
    3759              : 
    3760            0 :       int count = 0;
    3761            0 :       for (size_t i = 0; i < _trans_table.size(); i++) {
    3762            0 :          if (_trans_table[i].transition == transition) {
    3763            0 :             _trans_table[i].sequence_number = sequence_number;
    3764            0 :             count++;
    3765              :          }
    3766              :       }
    3767              : 
    3768            0 :       if (count == 0) {
    3769            0 :          cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
    3770            0 :          return CM_INVALID_TRANSITION;
    3771            0 :       } else if (count > 1) {
    3772            0 :          cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
    3773            0 :          return CM_INVALID_TRANSITION;
    3774              :       }
    3775              : 
    3776              :       /* Change local sequence number for this transition type */
    3777              : 
    3778            0 :       for (size_t i = 0; i < _trans_table.size(); i++) {
    3779            0 :          if (_trans_table[i].transition == transition) {
    3780            0 :             _trans_table[i].sequence_number = sequence_number;
    3781              :          }
    3782              :       }
    3783              : 
    3784              :       // implicit unlock
    3785            0 :    }
    3786              : 
    3787            0 :    cm_get_experiment_database(&hDB, &hKey);
    3788              : 
    3789              :    /* unlock database */
    3790            0 :    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
    3791              : 
    3792            0 :    sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
    3793              : 
    3794              :    /* set value */
    3795            0 :    status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
    3796            0 :    if (status != DB_SUCCESS)
    3797            0 :       return status;
    3798              : 
    3799              :    /* re-lock database */
    3800            0 :    db_set_mode(hDB, hKey, MODE_READ, TRUE);
    3801              : 
    3802            0 :    return CM_SUCCESS;
    3803              : 
    3804              : }
    3805              : 
    3806            0 : INT cm_set_client_run_state(INT state) {
    3807              :    INT status;
    3808              :    HNDLE hDB, hKey;
    3809              :    KEY key;
    3810              : 
    3811            0 :    cm_get_experiment_database(&hDB, &hKey);
    3812              : 
    3813              :    /* check that hKey is still valid */
    3814            0 :    status = db_get_key(hDB, hKey, &key);
    3815              : 
    3816            0 :    if (status != DB_SUCCESS) {
    3817            0 :       cm_msg(MERROR, "cm_set_client_run_state",
    3818              :              "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
    3819              :              hKey);
    3820            0 :       return status;
    3821              :    }
    3822              : 
    3823              :    /* unlock database */
    3824            0 :    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
    3825              : 
    3826              :    /* set value */
    3827            0 :    status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
    3828            0 :    if (status != DB_SUCCESS)
    3829            0 :       return status;
    3830              : 
    3831              :    /* re-lock database */
    3832            0 :    db_set_mode(hDB, hKey, MODE_READ, TRUE);
    3833              : 
    3834            0 :    return CM_SUCCESS;
    3835              : 
    3836              : }
    3837              : 
    3838              : /**dox***************************************************************/
    3839              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    3840              : 
    3841              : static INT _requested_transition;
    3842              : static DWORD _deferred_transition_mask;
    3843              : 
    3844              : /**dox***************************************************************/
    3845              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    3846              : 
    3847              : /********************************************************************/
    3848              : /**
    3849              : Register a deferred transition handler. If a client is
    3850              : registered as a deferred transition handler, it may defer
    3851              : a requested transition by returning FALSE until a certain
    3852              : condition (like a motor reaches its end position) is
    3853              : reached.
    3854              : @param transition      One of TR_xxx
    3855              : @param (*func)         Function which gets called whenever
    3856              :                        a transition is requested. If it returns
    3857              :                        FALSE, the transition is not performed.
    3858              : @return CM_SUCCESS,    \<error\> Error from ODB access
    3859              : */
    3860            0 : INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL)) {
    3861              :    INT status, size;
    3862              :    char tr_key_name[256];
    3863              :    HNDLE hDB, hKey;
    3864              : 
    3865            0 :    cm_get_experiment_database(&hDB, &hKey);
    3866              : 
    3867            0 :    for (int i = 0; _deferred_trans_table[i].transition; i++)
    3868            0 :       if (_deferred_trans_table[i].transition == transition)
    3869            0 :          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
    3870              : 
    3871              :    /* set new transition mask */
    3872            0 :    _deferred_transition_mask |= transition;
    3873              : 
    3874            0 :    sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
    3875              : 
    3876              :    /* unlock database */
    3877            0 :    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
    3878              : 
    3879              :    /* set value */
    3880            0 :    int i = 0;
    3881            0 :    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
    3882            0 :    if (status != DB_SUCCESS)
    3883            0 :       return status;
    3884              : 
    3885              :    /* re-lock database */
    3886            0 :    db_set_mode(hDB, hKey, MODE_READ, TRUE);
    3887              : 
    3888              :    /* hot link requested transition */
    3889            0 :    size = sizeof(_requested_transition);
    3890            0 :    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
    3891            0 :    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
    3892            0 :    status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
    3893            0 :    if (status != DB_SUCCESS) {
    3894            0 :       cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
    3895            0 :       return status;
    3896              :    }
    3897              : 
    3898            0 :    return CM_SUCCESS;
    3899              : }
    3900              : 
    3901              : /********************************************************************/
    3902              : /**
    3903              : Check for any deferred transition. If a deferred transition
    3904              : handler has been registered via the
    3905              : cm_register_deferred_transition function, this routine
    3906              : should be called regularly. It checks if a transition
    3907              : request is pending. If so, it calld the registered handler
    3908              : if the transition should be done and then actually does
    3909              : the transition.
    3910              : @return     CM_SUCCESS, \<error\>  Error from cm_transition()
    3911              : */
    3912            0 : INT cm_check_deferred_transition() {
    3913              :    INT i, status;
    3914              :    char str[256];
    3915              :    static BOOL first;
    3916              : 
    3917            0 :    if (_requested_transition == 0)
    3918            0 :       first = TRUE;
    3919              : 
    3920            0 :    if (_requested_transition & _deferred_transition_mask) {
    3921            0 :       for (i = 0; _deferred_trans_table[i].transition; i++)
    3922            0 :          if (_deferred_trans_table[i].transition == _requested_transition)
    3923            0 :             break;
    3924              : 
    3925            0 :       if (_deferred_trans_table[i].transition == _requested_transition) {
    3926            0 :          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func)(_requested_transition, first)) {
    3927            0 :             status = cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str), TR_SYNC, FALSE);
    3928            0 :             if (status != CM_SUCCESS)
    3929            0 :                cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
    3930              : 
    3931              :             /* bypass hotlink and set _requested_transition directly to zero */
    3932            0 :             _requested_transition = 0;
    3933              : 
    3934            0 :             return status;
    3935              :          }
    3936            0 :          first = FALSE;
    3937              :       }
    3938              :    }
    3939              : 
    3940            0 :    return SUCCESS;
    3941              : }
    3942              : 
    3943              : 
    3944              : /**dox***************************************************************/
    3945              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    3946              : 
    3947              : /********************************************************************/
    3948              : 
    3949              : /**dox***************************************************************/
    3950              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    3951              : 
    3952              : struct TrClient {
    3953              :    int transition = 0;
    3954              :    int run_number = 0;
    3955              :    int async_flag = 0;
    3956              :    int debug_flag = 0;
    3957              :    int sequence_number = 0;
    3958              :    std::vector<int> wait_for_index;
    3959              :    std::string host_name;
    3960              :    std::string client_name;
    3961              :    int port = 0;
    3962              :    std::string key_name; /* this client key name in /System/Clients */
    3963              :    std::atomic_int status{0};
    3964              :    std::thread* thread = NULL;
    3965              :    std::string errorstr;
    3966              :    DWORD init_time = 0;    // time when tr_client created
    3967              :    std::string waiting_for_client; // name of client we are waiting for
    3968              :    DWORD connect_timeout = 0;
    3969              :    DWORD connect_start_time = 0; // time when client rpc connection is started
    3970              :    DWORD connect_end_time = 0;   // time when client rpc connection is finished
    3971              :    DWORD rpc_timeout = 0;
    3972              :    DWORD rpc_start_time = 0;     // time client rpc call is started
    3973              :    DWORD rpc_end_time = 0;       // time client rpc call is finished
    3974              :    DWORD end_time = 0;           // time client thread is finished
    3975              : 
    3976            0 :    TrClient() // ctor
    3977            0 :    {
    3978              :       // empty
    3979            0 :    }
    3980              : 
    3981            0 :    ~TrClient() // dtor
    3982              :    {
    3983              :       //printf("TrClient::dtor: client \"%s\"\n", client_name);
    3984            0 :       assert(thread == NULL);
    3985            0 :    }
    3986              : 
    3987              :    void Print() const
    3988              :    {
    3989              :       printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
    3990              :       if (wait_for_index.size() > 0) {
    3991              :          printf(", wait for:");
    3992              :          for (size_t i=0; i<wait_for_index.size(); i++) {
    3993              :             printf(" %d", wait_for_index[i]);
    3994              :          }
    3995              :       }
    3996              :    }
    3997              : };
    3998              : 
    3999            0 : static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
    4000            0 :    return arg1->sequence_number < arg2->sequence_number;
    4001              : }
    4002              : 
    4003              : /*------------------------------------------------------------------*/
    4004              : 
    4005              : struct TrState {
    4006              :    int transition = 0;
    4007              :    int run_number = 0;
    4008              :    int async_flag = 0;
    4009              :    int debug_flag = 0;
    4010              :    int status     = 0;
    4011              :    std::string errorstr;
    4012              :    DWORD start_time = 0;
    4013              :    DWORD end_time   = 0;
    4014              :    std::vector<std::unique_ptr<TrClient>> clients;
    4015              : };
    4016              : 
    4017              : /*------------------------------------------------------------------*/
    4018              : 
    4019            0 : static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
    4020              : {
    4021            0 :    DWORD end_time = ss_millitime();
    4022              : 
    4023            0 :    if (transition != TR_STARTABORT) {
    4024            0 :       db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
    4025            0 :       db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
    4026              : 
    4027            0 :       if (errorstr) {
    4028            0 :          db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
    4029            0 :       } else if (status == CM_SUCCESS) {
    4030            0 :          const char *buf = "Success";
    4031            0 :          db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
    4032              :       } else {
    4033              :          char buf[256];
    4034            0 :          sprintf(buf, "status %d", status);
    4035            0 :          db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
    4036              :       }
    4037              :    }
    4038              : 
    4039            0 :    tr->status = status;
    4040            0 :    tr->end_time = end_time;
    4041            0 :    if (errorstr) {
    4042            0 :       tr->errorstr = errorstr;
    4043              :    } else {
    4044            0 :       tr->errorstr = "(null)";
    4045              :    }
    4046              : 
    4047            0 :    return status;
    4048              : }
    4049              : 
    4050              : /*------------------------------------------------------------------*/
    4051              : 
    4052            0 : static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client) {
    4053              :    //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
    4054              : 
    4055              :    int status;
    4056              :    HNDLE hKey;
    4057              : 
    4058            0 :    if (tr_client->transition == TR_STARTABORT) {
    4059            0 :       status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
    4060            0 :       status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
    4061            0 :       if (status != DB_SUCCESS)
    4062            0 :          return;
    4063              :    } else {
    4064            0 :       status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
    4065            0 :       status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
    4066            0 :       if (status != DB_SUCCESS)
    4067            0 :          return;
    4068              :    }
    4069              : 
    4070              :    // same client_name can exist with different sequence numbers!
    4071            0 :    std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
    4072              : 
    4073            0 :    status = db_create_key(hDB, hKey, keyname.c_str(), TID_KEY);
    4074            0 :    status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
    4075            0 :    if (status != DB_SUCCESS)
    4076            0 :       return;
    4077              :    
    4078            0 :    DWORD now = ss_millitime();
    4079              : 
    4080              :    //int   transition;
    4081              :    //int   run_number;
    4082              :    //int   async_flag;
    4083              :    //int   debug_flag;
    4084            0 :    status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
    4085            0 :    status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
    4086            0 :    status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
    4087            0 :    status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
    4088            0 :    status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
    4089            0 :    status = db_set_value(hDB, hKey, "waiting_for_client", tr_client->waiting_for_client.c_str(), tr_client->waiting_for_client.length() + 1, 1, TID_STRING);
    4090            0 :    status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
    4091            0 :    status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
    4092            0 :    status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
    4093            0 :    status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
    4094            0 :    status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
    4095            0 :    status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
    4096            0 :    status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
    4097            0 :    status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
    4098            0 :    status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
    4099            0 :    status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
    4100            0 : }
    4101              : 
    4102              : /*------------------------------------------------------------------*/
    4103              : 
    4104              : /* Perform a detached transition through the external "mtransition" program */
    4105            0 : static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
    4106              :    HNDLE hDB;
    4107              :    int status;
    4108              :    const char *args[100];
    4109            0 :    std::string path;
    4110              :    char debug_arg[256];
    4111              :    char start_arg[256];
    4112            0 :    std::string expt_name;
    4113            0 :    std::string mserver_hostname;
    4114              : 
    4115            0 :    int iarg = 0;
    4116              : 
    4117            0 :    cm_get_experiment_database(&hDB, NULL);
    4118              : 
    4119            0 :    const char *midassys = getenv("MIDASSYS");
    4120            0 :    if (midassys) {
    4121            0 :       path += midassys;
    4122            0 :       path += DIR_SEPARATOR_STR;
    4123            0 :       path += "bin";
    4124            0 :       path += DIR_SEPARATOR_STR;
    4125              :    }
    4126            0 :    path += "mtransition";
    4127              : 
    4128            0 :    args[iarg++] = path.c_str();
    4129              : 
    4130            0 :    if (rpc_is_remote()) {
    4131              :       /* if connected to mserver, pass connection info to mtransition */
    4132            0 :       mserver_hostname = rpc_get_mserver_hostname();
    4133            0 :       args[iarg++] = "-h";
    4134            0 :       args[iarg++] = mserver_hostname.c_str();
    4135              :    }
    4136              : 
    4137              :    /* get experiment name from ODB */
    4138            0 :    db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
    4139              : 
    4140            0 :    if (expt_name.length() > 0) {
    4141            0 :       args[iarg++] = "-e";
    4142            0 :       args[iarg++] = expt_name.c_str();
    4143              :    }
    4144              : 
    4145            0 :    if (debug_flag) {
    4146            0 :       args[iarg++] = "-d";
    4147              : 
    4148            0 :       sprintf(debug_arg, "%d", debug_flag);
    4149            0 :       args[iarg++] = debug_arg;
    4150              :    }
    4151              : 
    4152            0 :    if (transition == TR_STOP)
    4153            0 :       args[iarg++] = "STOP";
    4154            0 :    else if (transition == TR_PAUSE)
    4155            0 :       args[iarg++] = "PAUSE";
    4156            0 :    else if (transition == TR_RESUME)
    4157            0 :       args[iarg++] = "RESUME";
    4158            0 :    else if (transition == TR_START) {
    4159            0 :       args[iarg++] = "START";
    4160              : 
    4161            0 :       sprintf(start_arg, "%d", run_number);
    4162            0 :       args[iarg++] = start_arg;
    4163              :    }
    4164              : 
    4165            0 :    args[iarg++] = NULL;
    4166              : 
    4167              : #if 0
    4168              :    for (iarg = 0; args[iarg] != NULL; iarg++) {
    4169              :       printf("arg[%d] [%s]\n", iarg, args[iarg]);
    4170              :    }
    4171              : #endif
    4172              : 
    4173            0 :    status = ss_spawnv(P_DETACH, args[0], args);
    4174              : 
    4175            0 :    if (status != SS_SUCCESS) {
    4176            0 :       if (errstr != NULL) {
    4177            0 :          sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
    4178              :       }
    4179            0 :       return CM_SET_ERROR;
    4180              :    }
    4181              : 
    4182            0 :    return CM_SUCCESS;
    4183            0 : }
    4184              : 
    4185              : /*------------------------------------------------------------------*/
    4186              : 
    4187              : /* contact a client via RPC and execute the remote transition */
    4188            0 : static int cm_transition_call(TrState* s, int idx) {
    4189              :    INT old_timeout, status, i, t1, t0, size;
    4190              :    HNDLE hDB;
    4191            0 :    HNDLE hConn = -1;
    4192            0 :    int connect_timeout = 10000;
    4193            0 :    int timeout = 120000;
    4194              : 
    4195            0 :    cm_get_experiment_database(&hDB, NULL);
    4196            0 :    assert(hDB);
    4197              : 
    4198            0 :    TrClient *tr_client = s->clients[idx].get();
    4199              : 
    4200            0 :    tr_client->errorstr = "";
    4201              :    //tr_client->init_time = ss_millitime();
    4202            0 :    tr_client->waiting_for_client = "";
    4203            0 :    tr_client->connect_timeout = 0;
    4204            0 :    tr_client->connect_start_time = 0;
    4205            0 :    tr_client->connect_end_time = 0;
    4206            0 :    tr_client->rpc_timeout = 0;
    4207            0 :    tr_client->rpc_start_time = 0;
    4208            0 :    tr_client->rpc_end_time = 0;
    4209            0 :    tr_client->end_time = 0;
    4210              : 
    4211            0 :    write_tr_client_to_odb(hDB, tr_client);
    4212              : 
    4213              :    /* wait for predecessor if set */
    4214            0 :    if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
    4215              :       while (1) {
    4216            0 :          TrClient* wait_for = NULL;
    4217              : 
    4218            0 :          for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
    4219            0 :             int wait_for_index = tr_client->wait_for_index[i];
    4220              : 
    4221            0 :             assert(wait_for_index >= 0);
    4222            0 :             assert(wait_for_index < (int)s->clients.size());
    4223              : 
    4224            0 :             TrClient *t = s->clients[wait_for_index].get();
    4225              : 
    4226            0 :             if (!t)
    4227            0 :                continue;
    4228              : 
    4229            0 :             if (t->status == 0) {
    4230            0 :                wait_for = t;
    4231            0 :                break;
    4232              :             }
    4233              : 
    4234            0 :             if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
    4235            0 :                cm_msg(MERROR, "cm_transition_call", "Transition %d aborted: client \"%s\" returned status %d", tr_client->transition, t->client_name.c_str(), int(t->status));
    4236            0 :                tr_client->status = -1;
    4237            0 :                tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
    4238            0 :                tr_client->end_time = ss_millitime();
    4239            0 :                write_tr_client_to_odb(hDB, tr_client);
    4240            0 :                return CM_SUCCESS;
    4241              :             }
    4242              :          }
    4243              : 
    4244            0 :          if (wait_for == NULL)
    4245            0 :             break;
    4246              : 
    4247            0 :          tr_client->waiting_for_client = wait_for->client_name;
    4248            0 :          write_tr_client_to_odb(hDB, tr_client);
    4249              : 
    4250            0 :          if (tr_client->debug_flag == 1)
    4251            0 :             printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
    4252              : 
    4253            0 :          i = 0;
    4254            0 :          size = sizeof(i);
    4255            0 :          status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
    4256              : 
    4257            0 :          if (status == DB_SUCCESS && i == 0) {
    4258            0 :             cm_msg(MERROR, "cm_transition_call", "Client \"%s\" transition %d aborted while waiting for client \"%s\": \"/Runinfo/Transition in progress\" was cleared", tr_client->client_name.c_str(), tr_client->transition, wait_for->client_name.c_str());
    4259            0 :             tr_client->status = -1;
    4260            0 :             tr_client->errorstr = "Canceled";
    4261            0 :             tr_client->end_time = ss_millitime();
    4262            0 :             write_tr_client_to_odb(hDB, tr_client);
    4263            0 :             return CM_SUCCESS;
    4264              :          }
    4265              : 
    4266            0 :          ss_sleep(100);
    4267            0 :       };
    4268              :    }
    4269              : 
    4270            0 :    tr_client->waiting_for_client[0] = 0;
    4271              : 
    4272              :    /* contact client if transition mask set */
    4273            0 :    if (tr_client->debug_flag == 1)
    4274            0 :       printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4275            0 :    if (tr_client->debug_flag == 2)
    4276            0 :       cm_msg(MINFO, "cm_transition_call", "cm_transition_call: Connecting to client \"%s\" on host %s...", tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4277              : 
    4278              :    /* get transition timeout for rpc connect */
    4279            0 :    size = sizeof(timeout);
    4280            0 :    db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
    4281              : 
    4282            0 :    if (connect_timeout < 1000)
    4283            0 :       connect_timeout = 1000;
    4284              : 
    4285              :    /* get transition timeout */
    4286            0 :    size = sizeof(timeout);
    4287            0 :    db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
    4288              : 
    4289            0 :    if (timeout < 1000)
    4290            0 :       timeout = 1000;
    4291              : 
    4292              :    /* set our timeout for rpc_client_connect() */
    4293              :    //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
    4294            0 :    rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
    4295              : 
    4296            0 :    tr_client->connect_timeout = connect_timeout;
    4297            0 :    tr_client->connect_start_time = ss_millitime();
    4298              : 
    4299            0 :    write_tr_client_to_odb(hDB, tr_client);
    4300              : 
    4301              :    /* client found -> connect to its server port */
    4302            0 :    status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
    4303              : 
    4304            0 :    rpc_set_timeout(RPC_HNDLE_CONNECT, old_timeout);
    4305              : 
    4306            0 :    tr_client->connect_end_time = ss_millitime();
    4307            0 :    write_tr_client_to_odb(hDB, tr_client);
    4308              : 
    4309            0 :    if (status != RPC_SUCCESS) {
    4310            0 :       cm_msg(MERROR, "cm_transition_call",
    4311              :              "cannot connect to client \"%s\" on host %s, port %d, status %d",
    4312              :              tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
    4313            0 :       tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
    4314              : 
    4315              :       /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
    4316            0 :       cm_shutdown(tr_client->client_name.c_str(), TRUE);
    4317            0 :       cm_cleanup(tr_client->client_name.c_str(), TRUE);
    4318              : 
    4319            0 :       if (tr_client->transition != TR_STOP) {
    4320              :          /* indicate abort */
    4321            0 :          i = 1;
    4322            0 :          db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
    4323            0 :          i = 0;
    4324            0 :          db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
    4325              :       }
    4326              : 
    4327            0 :       tr_client->status = status;
    4328            0 :       tr_client->end_time = ss_millitime();
    4329              : 
    4330            0 :       write_tr_client_to_odb(hDB, tr_client);
    4331            0 :       return status;
    4332              :    }
    4333              : 
    4334            0 :    if (tr_client->debug_flag == 1)
    4335            0 :       printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4336            0 :    if (tr_client->debug_flag == 2)
    4337            0 :       cm_msg(MINFO, "cm_transition_call",
    4338              :              "cm_transition: Connection established to client \"%s\" on host %s",
    4339              :              tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4340              : 
    4341              :    /* call RC_TRANSITION on remote client with increased timeout */
    4342              :    //old_timeout = rpc_get_timeout(hConn);
    4343            0 :    rpc_set_timeout(hConn, timeout, &old_timeout);
    4344              : 
    4345            0 :    tr_client->rpc_timeout = timeout;
    4346            0 :    tr_client->rpc_start_time = ss_millitime();
    4347            0 :    write_tr_client_to_odb(hDB, tr_client);
    4348              : 
    4349            0 :    if (tr_client->debug_flag == 1)
    4350            0 :       printf("Executing RPC transition client \"%s\" on host %s...\n",
    4351              :              tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4352            0 :    if (tr_client->debug_flag == 2)
    4353            0 :       cm_msg(MINFO, "cm_transition_call",
    4354              :              "cm_transition: Executing RPC transition client \"%s\" on host %s...",
    4355              :              tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4356              : 
    4357            0 :    t0 = ss_millitime();
    4358              : 
    4359              :    char errorstr[TRANSITION_ERROR_STRING_LENGTH];
    4360            0 :    errorstr[0] = 0;
    4361              : 
    4362            0 :    status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
    4363              : 
    4364            0 :    tr_client->errorstr = errorstr;
    4365              : 
    4366            0 :    t1 = ss_millitime();
    4367              : 
    4368            0 :    tr_client->rpc_end_time = ss_millitime();
    4369              : 
    4370            0 :    write_tr_client_to_odb(hDB, tr_client);
    4371              : 
    4372              :    /* fix for clients returning 0 as error code */
    4373            0 :    if (status == 0)
    4374            0 :       status = FE_ERR_HW;
    4375              : 
    4376              :    /* reset timeout */
    4377            0 :    rpc_set_timeout(hConn, old_timeout);
    4378              : 
    4379              :    //DWORD t2 = ss_millitime();
    4380              : 
    4381            0 :    if (tr_client->debug_flag == 1)
    4382            0 :       printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
    4383              :              tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
    4384            0 :    if (tr_client->debug_flag == 2)
    4385            0 :       cm_msg(MINFO, "cm_transition_call",
    4386              :              "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
    4387              :              tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
    4388              : 
    4389            0 :    if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
    4390            0 :       tr_client->errorstr = msprintf("RPC network error or timeout from client \'%s\' on host \"%s\"", tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4391              :       /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
    4392            0 :       cm_shutdown(tr_client->client_name.c_str(), TRUE);
    4393            0 :       cm_cleanup(tr_client->client_name.c_str(), TRUE);
    4394            0 :    } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
    4395            0 :       tr_client->errorstr = msprintf("Unknown error %d from client \'%s\' on host \"%s\"", status, tr_client->client_name.c_str(), tr_client->host_name.c_str());
    4396              :    }
    4397              : 
    4398            0 :    tr_client->status = status;
    4399            0 :    tr_client->end_time = ss_millitime();
    4400              : 
    4401              :    // write updated status and end_time to ODB
    4402              : 
    4403            0 :    write_tr_client_to_odb(hDB, tr_client);
    4404              : 
    4405              : #if 0
    4406              :    printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
    4407              :           hConn,
    4408              :           tr_client->client_name.c_str(),
    4409              :           tr_client->init_time - tr_client->init_time,
    4410              :           tr_client->connect_start_time - tr_client->init_time,
    4411              :           tr_client->connect_end_time - tr_client->init_time,
    4412              :           tr_client->rpc_start_time - tr_client->init_time,
    4413              :           tr_client->rpc_end_time - tr_client->init_time,
    4414              :           t2 - tr_client->init_time,
    4415              :           tr_client->end_time - tr_client->init_time);
    4416              : #endif
    4417              : 
    4418            0 :    return CM_SUCCESS;
    4419              : }
    4420              : 
    4421              : /*------------------------------------------------------------------*/
    4422              : 
    4423            0 : static int cm_transition_call_direct(TrClient *tr_client)
    4424              : {
    4425              :    HNDLE hDB;
    4426              : 
    4427            0 :    cm_get_experiment_database(&hDB, NULL);
    4428              : 
    4429            0 :    DWORD now = ss_millitime();
    4430              : 
    4431            0 :    tr_client->errorstr = "";
    4432              :    //tr_client->init_time = now;
    4433            0 :    tr_client->waiting_for_client = "";
    4434            0 :    tr_client->connect_timeout = 0;
    4435            0 :    tr_client->connect_start_time = now;
    4436            0 :    tr_client->connect_end_time = now;
    4437            0 :    tr_client->rpc_timeout = 0;
    4438            0 :    tr_client->rpc_start_time = 0;
    4439            0 :    tr_client->rpc_end_time = 0;
    4440            0 :    tr_client->end_time = 0;
    4441              : 
    4442            0 :    write_tr_client_to_odb(hDB, tr_client);
    4443              : 
    4444              :    // find registered handler
    4445              :    // NB: this code should match same code in rpc_transition_dispatch()
    4446              :    // NB: only use the first handler, this is how MIDAS always worked
    4447              :    // NB: we could run all handlers, but we can return the status and error string of only one of them.
    4448              : 
    4449            0 :    _trans_table_mutex.lock();
    4450            0 :    size_t n = _trans_table.size();
    4451            0 :    _trans_table_mutex.unlock();
    4452              : 
    4453            0 :    for (size_t i = 0; i < n; i++) {
    4454            0 :       _trans_table_mutex.lock();
    4455            0 :       TRANS_TABLE tt = _trans_table[i];
    4456            0 :       _trans_table_mutex.unlock();
    4457            0 :       if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
    4458              :          /* call registered function */
    4459            0 :          if (tt.func) {
    4460            0 :             if (tr_client->debug_flag == 1)
    4461            0 :                printf("Calling local transition callback\n");
    4462            0 :             if (tr_client->debug_flag == 2)
    4463            0 :                cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
    4464              :             
    4465            0 :             tr_client->rpc_start_time = ss_millitime();
    4466              : 
    4467            0 :             write_tr_client_to_odb(hDB, tr_client);
    4468              : 
    4469              :             char errorstr[TRANSITION_ERROR_STRING_LENGTH];
    4470            0 :             errorstr[0] = 0;
    4471              :             
    4472            0 :             tr_client->status = tt.func(tr_client->run_number, errorstr);
    4473              : 
    4474            0 :             tr_client->errorstr = errorstr;
    4475              :             
    4476            0 :             tr_client->rpc_end_time = ss_millitime();
    4477              :             
    4478            0 :             if (tr_client->debug_flag == 1)
    4479            0 :                printf("Local transition callback finished, status %d\n", int(tr_client->status));
    4480            0 :             if (tr_client->debug_flag == 2)
    4481            0 :                cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
    4482              : 
    4483            0 :             tr_client->end_time = ss_millitime();
    4484              : 
    4485              :             // write status and end_time to ODB
    4486              : 
    4487            0 :             write_tr_client_to_odb(hDB, tr_client);
    4488              : 
    4489            0 :             return tr_client->status;
    4490              :          }
    4491              :       }
    4492              :    }
    4493              : 
    4494            0 :    cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
    4495              : 
    4496            0 :    tr_client->status = CM_SUCCESS;
    4497            0 :    tr_client->end_time = ss_millitime();
    4498              : 
    4499              :    // write status and end_time to ODB
    4500              : 
    4501            0 :    write_tr_client_to_odb(hDB, tr_client);
    4502              : 
    4503            0 :    return CM_SUCCESS;
    4504              : }
    4505              : 
    4506              : /********************************************************************/
    4507              : /**
    4508              : Performs a run transition (Start/Stop/Pause/Resume).
    4509              : 
    4510              : Synchronous/Asynchronous flag.
    4511              : If set to TR_ASYNC, the transition is done
    4512              : asynchronously, meaning that clients are connected and told to execute their
    4513              : callback routine, but no result is awaited. The return value is
    4514              : specified by the transition callback function on the remote clients. If all callbacks
    4515              : can perform the transition, CM_SUCCESS is returned. If one callback cannot
    4516              : perform the transition, the return value of this callback is returned from
    4517              : cm_transition().
    4518              : The async_flag is usually FALSE so that transition callbacks can block a
    4519              : run transition in case of problems and return an error string. The only exception are
    4520              : situations where a run transition is performed automatically by a program which
    4521              : cannot block in a transition. For example the logger can cause a run stop when a
    4522              : disk is nearly full but it cannot block in the cm_transition() function since it
    4523              : has its own run stop callback which must flush buffers and close disk files and
    4524              : tapes.
    4525              : \code
    4526              : ...
    4527              :     i = 1;
    4528              :     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
    4529              : 
    4530              :       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
    4531              :       if (status != CM_SUCCESS)
    4532              :       {
    4533              :         // in case of error
    4534              :         printf("Error: %s\n", str);
    4535              :       }
    4536              :     ...
    4537              : \endcode
    4538              : @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
    4539              : @param run_number New run number. If zero, use current run number plus one.
    4540              : @param errstr returned error string.
    4541              : @param errstr_size Size of error string.
    4542              : @param async_flag TR_SYNC: synchronization flag (TR_SYNC:wait completion, TR_ASYNC: retun immediately)
    4543              : @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
    4544              : @return CM_SUCCESS, \<error\> error code from remote client
    4545              : */
    4546            0 : static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
    4547              : {
    4548              :    INT i, status, size, sequence_number, port, state;
    4549              :    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hKeyTrans;
    4550              :    DWORD seconds;
    4551              :    char tr_key_name[256];
    4552              :    KEY key;
    4553              :    BOOL deferred;
    4554              :    char xerrstr[TRANSITION_ERROR_STRING_LENGTH];
    4555              : 
    4556              :    //printf("cm_transition2: transition %d, run_number %d, errstr %p, errstr_size %d, async_flag %d, debug_flag %d\n", transition, run_number, errstr, errstr_size, async_flag, debug_flag);
    4557              : 
    4558              :    /* if needed, use internal error string */
    4559            0 :    if (!errstr) {
    4560            0 :       errstr = xerrstr;
    4561            0 :       errstr_size = sizeof(xerrstr);
    4562              :    }
    4563              : 
    4564              :    /* erase error string */
    4565            0 :    errstr[0] = 0;
    4566              : 
    4567              :    /* get key of local client */
    4568            0 :    cm_get_experiment_database(&hDB, &hKeylocal);
    4569              : 
    4570            0 :    deferred = (transition & TR_DEFERRED) > 0;
    4571            0 :    transition &= ~TR_DEFERRED;
    4572              : 
    4573              :    /* check for valid transition */
    4574            0 :    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME
    4575            0 :        && transition != TR_STARTABORT) {
    4576            0 :       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
    4577            0 :       mstrlcpy(errstr, "Invalid transition request", errstr_size);
    4578            0 :       return CM_INVALID_TRANSITION;
    4579              :    }
    4580              : 
    4581              :    /* check if transition in progress */
    4582            0 :    if (!deferred) {
    4583            0 :       i = 0;
    4584            0 :       size = sizeof(i);
    4585            0 :       db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
    4586            0 :       if (i == 1) {
    4587            0 :          if (errstr) {
    4588            0 :             sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
    4589            0 :             mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
    4590              :          }
    4591            0 :          cm_msg(MERROR, "cm_transition", "another transition is already in progress");
    4592            0 :          return CM_TRANSITION_IN_PROGRESS;
    4593              :       }
    4594              :    }
    4595              : 
    4596              :    /* indicate transition in progress */
    4597            0 :    i = transition;
    4598            0 :    db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
    4599              : 
    4600              :    /* clear run abort flag */
    4601            0 :    i = 0;
    4602            0 :    db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
    4603              : 
    4604              :    /* construct new transition state */
    4605              : 
    4606            0 :    TrState s;
    4607              :    
    4608            0 :    s.transition = transition;
    4609            0 :    s.run_number = run_number;
    4610            0 :    s.async_flag = async_flag;
    4611            0 :    s.debug_flag = debug_flag;
    4612            0 :    s.status = 0;
    4613            0 :    s.errorstr[0] = 0;
    4614            0 :    s.start_time = ss_millitime();
    4615            0 :    s.end_time = 0;
    4616              : 
    4617              :    /* construct the ODB tree /System/Transition */
    4618              : 
    4619            0 :    status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
    4620            0 :    if (status == DB_SUCCESS) {
    4621            0 :       db_delete_key(hDB, hKey, FALSE);
    4622              :    }
    4623              : 
    4624            0 :    if (transition != TR_STARTABORT) {
    4625            0 :       status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
    4626            0 :       if (status == DB_SUCCESS) {
    4627            0 :          db_delete_key(hDB, hKey, FALSE);
    4628              :       }
    4629              :    }
    4630              : 
    4631            0 :    if (transition != TR_STARTABORT) {
    4632            0 :       db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
    4633            0 :       db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
    4634            0 :       db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
    4635            0 :       db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
    4636            0 :       status = 0;
    4637            0 :       db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
    4638            0 :       db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
    4639            0 :       db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
    4640              :    }
    4641              : 
    4642              :    /* check for alarms */
    4643            0 :    i = 0;
    4644            0 :    size = sizeof(i);
    4645            0 :    db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
    4646            0 :    if (i == TRUE && transition == TR_START) {
    4647            0 :       al_check();
    4648            0 :       std::string alarms;
    4649            0 :       if (al_get_alarms(&alarms) > 0) {
    4650            0 :          cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
    4651            0 :          mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
    4652            0 :          mstrlcat(errstr, alarms.c_str(), errstr_size);
    4653            0 :          return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
    4654              :       }
    4655            0 :    }
    4656              : 
    4657              :    /* check for required programs */
    4658            0 :    i = 0;
    4659            0 :    size = sizeof(i);
    4660            0 :    db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
    4661            0 :    if (i == TRUE && transition == TR_START) {
    4662              : 
    4663              :       HNDLE hkeyroot, hkey;
    4664              : 
    4665              :       /* check /programs alarms */
    4666            0 :       db_find_key(hDB, 0, "/Programs", &hkeyroot);
    4667            0 :       if (hkeyroot) {
    4668            0 :          for (i = 0;; i++) {
    4669            0 :             BOOL program_info_required = FALSE;
    4670            0 :             status = db_enum_key(hDB, hkeyroot, i, &hkey);
    4671            0 :             if (status == DB_NO_MORE_SUBKEYS)
    4672            0 :                break;
    4673              : 
    4674            0 :             db_get_key(hDB, hkey, &key);
    4675              : 
    4676              :             /* don't check "execute on xxx" */
    4677            0 :             if (key.type != TID_KEY)
    4678            0 :                continue;
    4679              : 
    4680            0 :             size = sizeof(program_info_required);
    4681            0 :             status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
    4682            0 :             if (status != DB_SUCCESS) {
    4683            0 :                cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
    4684            0 :                continue;
    4685              :             }
    4686              : 
    4687            0 :             if (program_info_required) {
    4688            0 :                std::string name = rpc_get_name();
    4689            0 :                std::string str = name;
    4690            0 :                str.resize(strlen(key.name));
    4691            0 :                if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
    4692            0 :                   cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
    4693            0 :                   std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
    4694            0 :                   mstrlcpy(errstr, serrstr.c_str(), errstr_size);
    4695            0 :                   return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
    4696            0 :                }
    4697            0 :             }
    4698            0 :          }
    4699              :       }
    4700              :    }
    4701              : 
    4702              :    /* do detached transition via mtransition tool */
    4703            0 :    if (async_flag & TR_DETACH) {
    4704            0 :       status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
    4705            0 :       return tr_finish(hDB, &s, transition, status, errstr);
    4706              :    }
    4707              : 
    4708            0 :    mstrlcpy(errstr, "Unknown error", errstr_size);
    4709              : 
    4710            0 :    if (debug_flag == 0) {
    4711            0 :       size = sizeof(i);
    4712            0 :       db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
    4713              :    }
    4714              : 
    4715              :    /* if no run number is given, get it from ODB and increment it */
    4716            0 :    if (run_number == 0) {
    4717            0 :       size = sizeof(run_number);
    4718            0 :       status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
    4719            0 :       assert(status == SUCCESS);
    4720            0 :       if (transition == TR_START) {
    4721            0 :          run_number++;
    4722              :       }
    4723            0 :       s.run_number = run_number;
    4724              : 
    4725            0 :       if (transition != TR_STARTABORT) {
    4726            0 :          db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
    4727              :       }
    4728              :    }
    4729              : 
    4730            0 :    if (run_number <= 0) {
    4731            0 :       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
    4732            0 :       abort();
    4733              :    }
    4734              : 
    4735              :    /* Set new run number in ODB */
    4736            0 :    if (transition == TR_START) {
    4737            0 :       if (debug_flag == 1)
    4738            0 :          printf("Setting run number %d in ODB\n", run_number);
    4739            0 :       if (debug_flag == 2)
    4740            0 :          cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
    4741              : 
    4742            0 :       status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
    4743            0 :       if (status != DB_SUCCESS) {
    4744            0 :          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
    4745            0 :          abort();
    4746              :       }
    4747              :    }
    4748              : 
    4749            0 :    if (deferred) {
    4750            0 :       if (debug_flag == 1)
    4751            0 :          printf("Clearing /Runinfo/Requested transition\n");
    4752            0 :       if (debug_flag == 2)
    4753            0 :          cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
    4754              : 
    4755              :       /* remove transition request */
    4756            0 :       i = 0;
    4757            0 :       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
    4758              :    } else {
    4759            0 :       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
    4760            0 :       if (status != DB_SUCCESS) {
    4761            0 :          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
    4762            0 :          if (errstr)
    4763            0 :             mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
    4764            0 :          return tr_finish(hDB, &s, transition, status, errstr);
    4765              :       }
    4766              : 
    4767              :       /* check if deferred transition already in progress */
    4768            0 :       size = sizeof(i);
    4769            0 :       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
    4770            0 :       if (i) {
    4771            0 :          if (errstr) {
    4772            0 :             mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
    4773            0 :             mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
    4774              :          }
    4775            0 :          return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
    4776              :       }
    4777              : 
    4778            0 :       std::string trname = cm_transition_name(transition);
    4779              : 
    4780            0 :       sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
    4781              : 
    4782              :       /* search database for clients with deferred transition request */
    4783            0 :       for (i = 0, status = 0;; i++) {
    4784            0 :          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
    4785            0 :          if (status == DB_NO_MORE_SUBKEYS)
    4786            0 :             break;
    4787              : 
    4788            0 :          if (status == DB_SUCCESS) {
    4789            0 :             size = sizeof(sequence_number);
    4790            0 :             status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
    4791              : 
    4792              :             /* if registered for deferred transition, set flag in ODB and return */
    4793            0 :             if (status == DB_SUCCESS) {
    4794              :                char str[256];
    4795            0 :                size = NAME_LENGTH;
    4796            0 :                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
    4797              : 
    4798            0 :                if (debug_flag == 1)
    4799            0 :                   printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
    4800            0 :                if (debug_flag == 2)
    4801            0 :                   cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
    4802              : 
    4803            0 :                if (debug_flag == 1)
    4804            0 :                   printf("Setting /Runinfo/Requested transition\n");
    4805            0 :                if (debug_flag == 2)
    4806            0 :                   cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
    4807              : 
    4808              :                /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
    4809              :                 * will activate the deferred transition code in the frontend.
    4810              :                 * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
    4811              : 
    4812            0 :                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
    4813              : 
    4814            0 :                db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
    4815              : 
    4816            0 :                if (errstr)
    4817            0 :                   sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
    4818              : 
    4819            0 :                return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
    4820              :             }
    4821              :          }
    4822            0 :       }
    4823            0 :    }
    4824              : 
    4825              :    /* execute programs on start */
    4826            0 :    if (transition == TR_START) {
    4827              :       char str[256];
    4828            0 :       str[0] = 0;
    4829            0 :       size = sizeof(str);
    4830            0 :       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
    4831            0 :       if (str[0])
    4832            0 :          ss_system(str);
    4833              : 
    4834            0 :       db_find_key(hDB, 0, "/Programs", &hRootKey);
    4835            0 :       if (hRootKey) {
    4836            0 :          for (i = 0;; i++) {
    4837            0 :             BOOL program_info_auto_start = FALSE;
    4838            0 :             status = db_enum_key(hDB, hRootKey, i, &hKey);
    4839            0 :             if (status == DB_NO_MORE_SUBKEYS)
    4840            0 :                break;
    4841              : 
    4842            0 :             db_get_key(hDB, hKey, &key);
    4843              : 
    4844              :             /* don't check "execute on xxx" */
    4845            0 :             if (key.type != TID_KEY)
    4846            0 :                continue;
    4847              : 
    4848            0 :             size = sizeof(program_info_auto_start);
    4849            0 :             status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
    4850            0 :             if (status != DB_SUCCESS) {
    4851            0 :                cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
    4852            0 :                continue;
    4853              :             }
    4854              : 
    4855            0 :             if (program_info_auto_start) {
    4856              :                char start_command[MAX_STRING_LENGTH];
    4857            0 :                start_command[0] = 0;
    4858              : 
    4859            0 :                size = sizeof(start_command);
    4860            0 :                status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
    4861            0 :                if (status != DB_SUCCESS) {
    4862            0 :                   cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
    4863            0 :                   continue;
    4864              :                }
    4865              : 
    4866            0 :                if (start_command[0]) {
    4867            0 :                   cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
    4868              :                          start_command);
    4869            0 :                   ss_system(start_command);
    4870              :                }
    4871              :             }
    4872            0 :          }
    4873              :       }
    4874              :    }
    4875              : 
    4876              :    /* execute programs on startabort */
    4877            0 :    if (transition == TR_STARTABORT) {
    4878              :       /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
    4879            0 :       std::string cmd;
    4880            0 :       db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
    4881              :       
    4882            0 :       if (!cmd.empty())
    4883            0 :          ss_system(cmd.c_str());
    4884            0 :    }
    4885              : 
    4886              :    /* set new start time in database */
    4887            0 :    if (transition == TR_START) {
    4888              :       /* ASCII format */
    4889            0 :       std::string now = cm_asctime();
    4890            0 :       now.reserve(32);
    4891            0 :       db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
    4892              : 
    4893              :       /* reset stop time */
    4894            0 :       seconds = 0;
    4895            0 :       db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
    4896              : 
    4897              :       /* Seconds since 1.1.1970 */
    4898            0 :       cm_time(&seconds);
    4899            0 :       db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
    4900            0 :    }
    4901              : 
    4902            0 :    size = sizeof(state);
    4903            0 :    status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
    4904              : 
    4905              :    /* set stop time in database */
    4906            0 :    if (transition == TR_STOP) {
    4907            0 :       if (status != DB_SUCCESS)
    4908            0 :          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
    4909              : 
    4910            0 :       if (state != STATE_STOPPED) {
    4911              :          /* stop time binary */
    4912            0 :          cm_time(&seconds);
    4913            0 :          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
    4914            0 :          if (status != DB_SUCCESS)
    4915            0 :             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
    4916              : 
    4917              :          /* stop time ascii */
    4918            0 :          std::string now = cm_asctime();
    4919            0 :          now.reserve(32);
    4920            0 :          status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
    4921            0 :          if (status != DB_SUCCESS)
    4922            0 :             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
    4923            0 :       }
    4924              :    }
    4925              : 
    4926            0 :    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
    4927            0 :    if (status != DB_SUCCESS) {
    4928            0 :       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
    4929            0 :       if (errstr)
    4930            0 :          mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
    4931            0 :       return tr_finish(hDB, &s, transition, status, errstr);
    4932              :    }
    4933              : 
    4934            0 :    std::string trname = cm_transition_name(transition);
    4935              : 
    4936              :    /* check that all transition clients are alive */
    4937            0 :    for (int i = 0;;) {
    4938            0 :       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
    4939            0 :       if (status != DB_SUCCESS)
    4940            0 :          break;
    4941              : 
    4942            0 :       status = cm_check_client(hDB, hSubkey);
    4943              : 
    4944            0 :       if (status == DB_SUCCESS) {
    4945              :          /* this client is alive. Check next one! */
    4946            0 :          i++;
    4947            0 :          continue;
    4948              :       }
    4949              : 
    4950            0 :       assert(status == CM_NO_CLIENT);
    4951              : 
    4952              :       /* start from scratch: removing odb entries as we iterate over them
    4953              :        * does strange things to db_enum_key() */
    4954            0 :       i = 0;
    4955              :    }
    4956              : 
    4957              :    /* check for broken RPC connections */
    4958            0 :    rpc_client_check();
    4959              : 
    4960            0 :    if (debug_flag == 1)
    4961            0 :       printf("---- Transition %s started ----\n", trname.c_str());
    4962            0 :    if (debug_flag == 2)
    4963            0 :       cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
    4964              : 
    4965            0 :    sprintf(tr_key_name, "Transition %s", trname.c_str());
    4966              : 
    4967              :    /* search database for clients which registered for transition */
    4968              : 
    4969            0 :    for (int i = 0, status = 0;; i++) {
    4970              :       KEY subkey;
    4971            0 :       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
    4972            0 :       if (status == DB_NO_MORE_SUBKEYS)
    4973            0 :          break;
    4974              : 
    4975            0 :       status = db_get_key(hDB, hSubkey, &subkey);
    4976            0 :       assert(status == DB_SUCCESS);
    4977              : 
    4978            0 :       if (status == DB_SUCCESS) {
    4979            0 :          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
    4980              : 
    4981            0 :          if (status == DB_SUCCESS) {
    4982              : 
    4983            0 :             db_get_key(hDB, hKeyTrans, &key);
    4984              : 
    4985            0 :             for (int j = 0; j < key.num_values; j++) {
    4986            0 :                size = sizeof(sequence_number);
    4987            0 :                status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
    4988            0 :                assert(status == DB_SUCCESS);
    4989              : 
    4990            0 :                TrClient *c = new TrClient;
    4991              : 
    4992            0 :                c->init_time  = ss_millitime();
    4993            0 :                c->transition = transition;
    4994            0 :                c->run_number = run_number;
    4995            0 :                c->async_flag = async_flag;
    4996            0 :                c->debug_flag = debug_flag;
    4997            0 :                c->sequence_number = sequence_number;
    4998            0 :                c->status = 0;
    4999            0 :                c->key_name = subkey.name;
    5000              : 
    5001              :                /* get client info */
    5002              :                char client_name[NAME_LENGTH];
    5003            0 :                size = sizeof(client_name);
    5004            0 :                db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
    5005            0 :                c->client_name = client_name;
    5006              : 
    5007              :                char host_name[HOST_NAME_LENGTH];
    5008            0 :                size = sizeof(host_name);
    5009            0 :                db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
    5010            0 :                c->host_name = host_name;
    5011              : 
    5012              :                //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
    5013              : 
    5014            0 :                if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
    5015              :                   /* remember own client */
    5016            0 :                   c->port = 0;
    5017              :                } else {
    5018            0 :                   size = sizeof(port);
    5019            0 :                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
    5020            0 :                   c->port = port;
    5021              :                }
    5022              : 
    5023              :                /* check for duplicates */
    5024              : 
    5025            0 :                bool found = false;
    5026            0 :                for (size_t k=0; k<s.clients.size(); k++) {
    5027            0 :                   TrClient* cc = s.clients[k].get();
    5028            0 :                   if (cc->client_name == c->client_name)
    5029            0 :                      if (cc->host_name == c->host_name)
    5030            0 :                         if (cc->port == c->port)
    5031            0 :                            if (cc->sequence_number == c->sequence_number)
    5032            0 :                               found = true;
    5033              :                }
    5034              : 
    5035            0 :                if (!found) {
    5036            0 :                   s.clients.push_back(std::unique_ptr<TrClient>(c));
    5037            0 :                   c = NULL;
    5038              :                } else {
    5039            0 :                   cm_msg(MERROR, "cm_transition", "transition %s: client \"%s\" is registered with sequence number %d more than once", trname.c_str(), c->client_name.c_str(), c->sequence_number);
    5040            0 :                   delete c;
    5041            0 :                   c = NULL;
    5042              :                }
    5043              :             }
    5044              :          }
    5045              :       }
    5046            0 :    }
    5047              : 
    5048            0 :    std::sort(s.clients.begin(), s.clients.end(), tr_compare);
    5049              : 
    5050              :    /* set predecessor for multi-threaded transitions */
    5051            0 :    for (size_t idx = 0; idx < s.clients.size(); idx++) {
    5052            0 :       if (s.clients[idx]->sequence_number == 0) {
    5053              :          // sequence number 0 means "don't care"
    5054              :       } else {
    5055              :          /* find clients with smaller sequence number */
    5056            0 :          if (idx > 0) {
    5057            0 :             for (size_t i = idx - 1; ; i--) {
    5058            0 :                if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
    5059            0 :                   if (s.clients[i]->sequence_number > 0) {
    5060            0 :                      s.clients[idx]->wait_for_index.push_back(i);
    5061              :                   }
    5062              :                }
    5063            0 :                if (i==0)
    5064            0 :                   break;
    5065              :             }
    5066              :          }
    5067              :       }
    5068              :    }
    5069              : 
    5070            0 :    for (size_t idx = 0; idx < s.clients.size(); idx++) {
    5071            0 :       write_tr_client_to_odb(hDB, s.clients[idx].get());
    5072              :    }
    5073              : 
    5074              : #if 0
    5075              :    for (size_t idx = 0; idx < s.clients.size(); idx++) {
    5076              :       printf("TrClient[%d]: ", int(idx));
    5077              :       s.clients[idx]->Print();
    5078              :       printf("\n");
    5079              :    }
    5080              : #endif
    5081              : 
    5082              :    /* contact ordered clients for transition -----------------------*/
    5083            0 :    status = CM_SUCCESS;
    5084            0 :    for (size_t idx = 0; idx < s.clients.size(); idx++) {
    5085            0 :       if (debug_flag == 1)
    5086            0 :          printf("\n==== Found client \"%s\" with sequence number %d\n",
    5087            0 :                 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
    5088            0 :       if (debug_flag == 2)
    5089            0 :          cm_msg(MINFO, "cm_transition",
    5090              :                 "cm_transition: ==== Found client \"%s\" with sequence number %d",
    5091            0 :                 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
    5092              : 
    5093            0 :       if (async_flag & TR_MTHREAD) {
    5094            0 :          status = CM_SUCCESS;
    5095            0 :          assert(s.clients[idx]->thread == NULL);
    5096            0 :          s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
    5097              :       } else {
    5098            0 :          if (s.clients[idx]->port == 0) {
    5099              :             /* if own client call transition callback directly */
    5100            0 :             status = cm_transition_call_direct(s.clients[idx].get());
    5101              :          } else {
    5102              :             /* if other client call transition via RPC layer */
    5103            0 :             status = cm_transition_call(&s, idx);
    5104              :          }
    5105              : 
    5106            0 :          if (status == CM_SUCCESS && transition != TR_STOP)
    5107            0 :             if (s.clients[idx]->status != SUCCESS) {
    5108            0 :                cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
    5109            0 :                       s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
    5110            0 :                break;
    5111              :             }
    5112              :       }
    5113              : 
    5114            0 :       if (status != CM_SUCCESS)
    5115            0 :          break;
    5116              :    }
    5117              : 
    5118              :    /* wait until all threads have finished */
    5119            0 :    for (size_t idx = 0; idx < s.clients.size(); idx++) {
    5120            0 :       if (s.clients[idx]->thread) {
    5121              :          // join() will wait forever until thread finishes
    5122            0 :          s.clients[idx]->thread->join();
    5123            0 :          delete s.clients[idx]->thread;
    5124            0 :          s.clients[idx]->thread = NULL;
    5125              :       }
    5126              :    }
    5127              : 
    5128              :    /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
    5129              : 
    5130            0 :    i = 0;
    5131            0 :    size = sizeof(i);
    5132            0 :    status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
    5133              : 
    5134            0 :    if (status == DB_SUCCESS && i == 0) {
    5135            0 :       cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
    5136              : 
    5137            0 :       if (errstr != NULL)
    5138            0 :          mstrlcpy(errstr, "Canceled", errstr_size);
    5139              : 
    5140            0 :       return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
    5141              :    }
    5142              : 
    5143              :    /* search for any error */
    5144            0 :    for (size_t idx = 0; idx < s.clients.size(); idx++)
    5145            0 :       if (s.clients[idx]->status != CM_SUCCESS) {
    5146            0 :          status = s.clients[idx]->status;
    5147            0 :          if (errstr)
    5148            0 :             mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
    5149            0 :          s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
    5150            0 :          break;
    5151              :       }
    5152              : 
    5153            0 :    if (transition != TR_STOP && status != CM_SUCCESS) {
    5154              :       /* indicate abort */
    5155            0 :       i = 1;
    5156            0 :       db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
    5157            0 :       i = 0;
    5158            0 :       db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
    5159              : 
    5160            0 :       return tr_finish(hDB, &s, transition, status, errstr);
    5161              :    }
    5162              : 
    5163            0 :    if (debug_flag == 1)
    5164            0 :       printf("\n---- Transition %s finished ----\n", trname.c_str());
    5165            0 :    if (debug_flag == 2)
    5166            0 :       cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
    5167              : 
    5168              :    /* set new run state in database */
    5169            0 :    if (transition == TR_START || transition == TR_RESUME)
    5170            0 :       state = STATE_RUNNING;
    5171              : 
    5172            0 :    if (transition == TR_PAUSE)
    5173            0 :       state = STATE_PAUSED;
    5174              : 
    5175            0 :    if (transition == TR_STOP)
    5176            0 :       state = STATE_STOPPED;
    5177              : 
    5178            0 :    if (transition == TR_STARTABORT)
    5179            0 :       state = STATE_STOPPED;
    5180              : 
    5181            0 :    size = sizeof(state);
    5182            0 :    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
    5183            0 :    if (status != DB_SUCCESS)
    5184            0 :       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
    5185              : 
    5186              :    /* send notification message */
    5187            0 :    if (transition == TR_START)
    5188            0 :       cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
    5189            0 :    if (transition == TR_STOP)
    5190            0 :       cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
    5191            0 :    if (transition == TR_PAUSE)
    5192            0 :       cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
    5193            0 :    if (transition == TR_RESUME)
    5194            0 :       cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
    5195            0 :    if (transition == TR_STARTABORT)
    5196            0 :       cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
    5197              : 
    5198              :    /* lock/unlock ODB values if present */
    5199            0 :    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
    5200            0 :    if (hKey) {
    5201            0 :       if (state == STATE_STOPPED)
    5202            0 :          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
    5203              :       else
    5204            0 :          db_set_mode(hDB, hKey, MODE_READ, TRUE);
    5205              :    }
    5206              : 
    5207              :    /* flush online database */
    5208            0 :    if (transition == TR_STOP)
    5209            0 :       db_flush_database(hDB);
    5210              : 
    5211              :    /* execute/stop programs on stop */
    5212            0 :    if (transition == TR_STOP) {
    5213            0 :       std::string cmd;
    5214            0 :       db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
    5215            0 :       if (!cmd.empty())
    5216            0 :          ss_system(cmd.c_str());
    5217              : 
    5218            0 :       db_find_key(hDB, 0, "/Programs", &hRootKey);
    5219            0 :       if (hRootKey) {
    5220            0 :          for (i = 0;; i++) {
    5221            0 :             BOOL program_info_auto_stop = FALSE;
    5222            0 :             status = db_enum_key(hDB, hRootKey, i, &hKey);
    5223            0 :             if (status == DB_NO_MORE_SUBKEYS)
    5224            0 :                break;
    5225              : 
    5226            0 :             db_get_key(hDB, hKey, &key);
    5227              : 
    5228              :             /* don't check "execute on xxx" */
    5229            0 :             if (key.type != TID_KEY)
    5230            0 :                continue;
    5231              : 
    5232            0 :             size = sizeof(program_info_auto_stop);
    5233            0 :             status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
    5234            0 :             if (status != DB_SUCCESS) {
    5235            0 :                cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
    5236            0 :                continue;
    5237              :             }
    5238              : 
    5239            0 :             if (program_info_auto_stop) {
    5240            0 :                cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
    5241            0 :                cm_shutdown(key.name, FALSE);
    5242              :             }
    5243            0 :          }
    5244              :       }
    5245            0 :    }
    5246              : 
    5247              : 
    5248              :    /* indicate success */
    5249            0 :    i = 0;
    5250            0 :    db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
    5251              : 
    5252            0 :    if (errstr != NULL)
    5253            0 :       mstrlcpy(errstr, "Success", errstr_size);
    5254              : 
    5255            0 :    return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
    5256            0 : }
    5257              : 
    5258              : /*------------------------------------------------------------------*/
    5259              : 
    5260              : /* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
    5261            0 : static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
    5262              :    int status;
    5263              : 
    5264            0 :    status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
    5265              : 
    5266            0 :    if (transition == TR_START && status != CM_SUCCESS) {
    5267            0 :       cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
    5268              :              errstr);
    5269            0 :       cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
    5270              :    }
    5271              : 
    5272            0 :    return status;
    5273              : }
    5274              : 
    5275              : /*------------------------------------------------------------------*/
    5276              : 
    5277            0 : static INT tr_main_thread(void *param) {
    5278              :    INT status;
    5279              :    TR_PARAM *trp;
    5280              : 
    5281            0 :    trp = (TR_PARAM *) param;
    5282            0 :    status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
    5283              : 
    5284            0 :    trp->status = status;
    5285            0 :    trp->finished = TRUE;
    5286              : 
    5287            0 :    return 0;
    5288              : }
    5289              : 
    5290            0 : INT cm_transition_cleanup()
    5291              : {
    5292            0 :    if (_trp.thread && !_trp.finished) {
    5293              :       //printf("main transition thread did not finish yet!\n");
    5294            0 :       return CM_TRANSITION_IN_PROGRESS;
    5295              :    }
    5296              : 
    5297            0 :    std::thread* t = _trp.thread.exchange(NULL);
    5298              : 
    5299            0 :    if (t) {
    5300            0 :       t->join();
    5301            0 :       delete t;
    5302            0 :       t = NULL;
    5303              :    }
    5304              : 
    5305            0 :    return CM_SUCCESS;
    5306              : }
    5307              : 
    5308              : /* wrapper around cm_transition1() for detached multi-threaded transitions */
    5309            0 : INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
    5310            0 :    int mflag = async_flag & TR_MTHREAD;
    5311            0 :    int sflag = async_flag & TR_SYNC;
    5312              : 
    5313            0 :    int status = cm_transition_cleanup();
    5314              : 
    5315            0 :    if (status != CM_SUCCESS) {
    5316            0 :       cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
    5317            0 :       return CM_TRANSITION_IN_PROGRESS;
    5318              :    }
    5319              : 
    5320              :    /* get key of local client */
    5321              :    HNDLE hDB;
    5322            0 :    cm_get_experiment_database(&hDB, NULL);
    5323              : 
    5324            0 :    bool deferred = (transition & TR_DEFERRED) > 0;
    5325            0 :    INT trans_raw = (transition & ~TR_DEFERRED);
    5326              : 
    5327              :    /* check for valid transition */
    5328            0 :    if (trans_raw != TR_START && trans_raw != TR_STOP && trans_raw != TR_PAUSE && trans_raw != TR_RESUME && trans_raw != TR_STARTABORT) {
    5329            0 :       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
    5330            0 :       if (errstr) {
    5331            0 :          mstrlcpy(errstr, "Invalid transition request", errstr_size);
    5332              :       }
    5333            0 :       return CM_INVALID_TRANSITION;
    5334              :    }
    5335              : 
    5336              :    /* check if transition in progress */
    5337            0 :    if (!deferred) {
    5338            0 :       int i = 0;
    5339            0 :       int size = sizeof(i);
    5340            0 :       db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
    5341            0 :       if (i == 1) {
    5342            0 :          if (errstr) {
    5343            0 :             sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
    5344            0 :             mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
    5345              :          }
    5346            0 :          cm_msg(MERROR, "cm_transition", "another transition is already in progress");
    5347            0 :          return CM_TRANSITION_IN_PROGRESS;
    5348              :       }
    5349              :    }
    5350              : 
    5351            0 :    if (mflag) {
    5352            0 :       _trp.transition = transition;
    5353            0 :       _trp.run_number = run_number;
    5354            0 :       if (sflag) {
    5355              :          /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
    5356            0 :          _trp.errstr = errstr;
    5357            0 :          _trp.errstr_size = errstr_size;
    5358              :       } else {
    5359              :          /* in normal MTHREAD mode, we return right away and
    5360              :           * if errstr is a local variable in the caller and they return too,
    5361              :           * errstr becomes a stale reference and writing into it will corrupt the stack
    5362              :           * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
    5363              :           * and we definitely corrupt mlogger memory with out this: */
    5364            0 :          _trp.errstr = NULL;
    5365            0 :          _trp.errstr_size = 0;
    5366              :       }
    5367            0 :       _trp.async_flag = async_flag;
    5368            0 :       _trp.debug_flag = debug_flag;
    5369            0 :       _trp.status = 0;
    5370            0 :       _trp.finished = FALSE;
    5371              : 
    5372            0 :       if (errstr)
    5373            0 :          *errstr = 0; // null error string
    5374              : 
    5375              :       //ss_thread_create(tr_main_thread, &_trp);
    5376              : 
    5377            0 :       std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
    5378              : 
    5379            0 :       assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
    5380              : 
    5381            0 :       if (sflag) {
    5382              : 
    5383              :          /* wait until main thread has finished */
    5384              :          do {
    5385            0 :             ss_sleep(10);
    5386            0 :          } while (!_trp.finished);
    5387              : 
    5388            0 :          std::thread* t = _trp.thread.exchange(NULL);
    5389              : 
    5390            0 :          if (t) {
    5391            0 :             t->join();
    5392            0 :             delete t;
    5393            0 :             t = NULL;
    5394              :          }
    5395              : 
    5396            0 :          return _trp.status;
    5397              :       }
    5398              :    } else
    5399            0 :       return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
    5400              : 
    5401            0 :    return CM_SUCCESS;
    5402              : }
    5403              : 
    5404              : /**dox***************************************************************/
    5405              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    5406              : 
    5407              : /********************************************************************/
    5408            0 : INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
    5409              : /********************************************************************\
    5410              : 
    5411              :   Routine: cm_dispatch_ipc
    5412              : 
    5413              :   Purpose: Called from ss_suspend if an IPC message arrives
    5414              : 
    5415              :   Input:
    5416              :     INT   msg               IPC message we got, MSG_ODB/MSG_BM
    5417              :     INT   p1, p2            Optional parameters
    5418              :     int   s                 Optional server socket
    5419              : 
    5420              :   Output:
    5421              :     none
    5422              : 
    5423              :   Function value:
    5424              :     CM_SUCCESS              Successful completion
    5425              : 
    5426              : \********************************************************************/
    5427              : {
    5428            0 :    if (message[0] == 'O') {
    5429              :       HNDLE hDB, hKey, hKeyRoot;
    5430              :       INT index;
    5431            0 :       index = 0;
    5432            0 :       sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
    5433            0 :       if (client_socket) {
    5434            0 :          return db_update_record_mserver(hDB, hKeyRoot, hKey, index, client_socket);
    5435              :       } else {
    5436            0 :          return db_update_record_local(hDB, hKeyRoot, hKey, index);
    5437              :       }
    5438              :    }
    5439              : 
    5440              :    /* message == "B" means "resume event sender" */
    5441            0 :    if (message[0] == 'B' && message[2] != ' ') {
    5442              :       char str[NAME_LENGTH];
    5443              : 
    5444              :       //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
    5445              : 
    5446            0 :       mstrlcpy(str, message + 2, sizeof(str));
    5447            0 :       if (strchr(str, ' '))
    5448            0 :          *strchr(str, ' ') = 0;
    5449              : 
    5450            0 :       if (client_socket)
    5451            0 :          return bm_notify_client(str, client_socket);
    5452              :       else
    5453            0 :          return bm_push_event(str);
    5454              :    }
    5455              : 
    5456              :    //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
    5457              : 
    5458            0 :    return CM_SUCCESS;
    5459              : }
    5460              : 
    5461              : /********************************************************************/
    5462              : static BOOL _ctrlc_pressed = FALSE;
    5463              : 
    5464            0 : void cm_ctrlc_handler(int sig) {
    5465            0 :    if (_ctrlc_pressed) {
    5466            0 :       printf("Received 2nd Ctrl-C, hard abort\n");
    5467            0 :       exit(0);
    5468              :    }
    5469            0 :    printf("Received Ctrl-C, aborting...\n");
    5470            0 :    _ctrlc_pressed = TRUE;
    5471              : 
    5472            0 :    ss_ctrlc_handler(cm_ctrlc_handler);
    5473            0 : }
    5474              : 
    5475            0 : BOOL cm_is_ctrlc_pressed() {
    5476            0 :    return _ctrlc_pressed;
    5477              : }
    5478              : 
    5479            0 : void cm_ack_ctrlc_pressed() {
    5480            0 :    _ctrlc_pressed = FALSE;
    5481            0 : }
    5482              : 
    5483              : /********************************************************************/
    5484            0 : int cm_exec_script(const char *odb_path_to_script)
    5485              : /********************************************************************\
    5486              : 
    5487              :   Routine: cm_exec_script
    5488              : 
    5489              :   Purpose: Execute script from /Script tree
    5490              : 
    5491              :   exec_script is enabled by the tree /Script
    5492              :   The /Script struct is composed of list of keys
    5493              :   from which the name of the key is the button name
    5494              :   and the sub-structure is a record as follow:
    5495              : 
    5496              :   /Script/<button_name> = <script command> (TID_STRING)
    5497              : 
    5498              :   The "Script command", containing possible arguements,
    5499              :   is directly executed.
    5500              : 
    5501              :   /Script/<button_name>/<script command>
    5502              :                         <soft link1>|<arg1>
    5503              :                         <soft link2>|<arg2>
    5504              :                            ...
    5505              : 
    5506              :   The arguments for the script are derived from the
    5507              :   subtree below <button_name>, where <button_name> must be
    5508              :   TID_KEY. The subtree may then contain arguments or links
    5509              :   to other values in the ODB, like run number etc.
    5510              : 
    5511              : \********************************************************************/
    5512              : {
    5513              :    HNDLE hDB, hkey;
    5514              :    KEY key;
    5515              :    int status;
    5516              : 
    5517            0 :    status = cm_get_experiment_database(&hDB, NULL);
    5518            0 :    if (status != DB_SUCCESS)
    5519            0 :       return status;
    5520              : 
    5521            0 :    status = db_find_key(hDB, 0, odb_path_to_script, &hkey);
    5522            0 :    if (status != DB_SUCCESS)
    5523            0 :       return status;
    5524              : 
    5525            0 :    status = db_get_key(hDB, hkey, &key);
    5526            0 :    if (status != DB_SUCCESS)
    5527            0 :       return status;
    5528              : 
    5529            0 :    std::string command;
    5530              : 
    5531            0 :    if (key.type == TID_STRING) {
    5532            0 :       int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
    5533            0 :       if (status != DB_SUCCESS) {
    5534            0 :          cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
    5535              :                 odb_path_to_script, status);
    5536            0 :          return status;
    5537              :       }
    5538            0 :    } else if (key.type == TID_KEY) {
    5539            0 :       for (int i = 0;; i++) {
    5540              :          HNDLE hsubkey;
    5541              :          KEY subkey;
    5542            0 :          db_enum_key(hDB, hkey, i, &hsubkey);
    5543            0 :          if (!hsubkey)
    5544            0 :             break;
    5545            0 :          db_get_key(hDB, hsubkey, &subkey);
    5546              : 
    5547            0 :          if (i > 0)
    5548            0 :             command += " ";
    5549              : 
    5550            0 :          if (subkey.type == TID_KEY) {
    5551            0 :             cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
    5552              :                    subkey.name);
    5553            0 :             return DB_TYPE_MISMATCH;
    5554              :          } else {
    5555            0 :             int size = subkey.item_size;
    5556            0 :             char *buf = (char *) malloc(size);
    5557            0 :             assert(buf != NULL);
    5558            0 :             int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
    5559            0 :             if (status != DB_SUCCESS) {
    5560            0 :                cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
    5561              :                       odb_path_to_script, subkey.name, subkey.type, status);
    5562            0 :                free(buf);
    5563            0 :                return status;
    5564              :             }
    5565            0 :             if (subkey.type == TID_STRING) {
    5566            0 :                command += buf;
    5567              :             } else {
    5568            0 :                command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
    5569              :             }
    5570            0 :             free(buf);
    5571              :          }
    5572            0 :       }
    5573              :    } else {
    5574            0 :       cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
    5575              :              odb_path_to_script, key.type);
    5576            0 :       return DB_TYPE_MISMATCH;
    5577              :    }
    5578              : 
    5579              :    // printf("exec_script: %s\n", command.c_str());
    5580              : 
    5581            0 :    if (command.length() > 0) {
    5582            0 :       cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
    5583            0 :       ss_system(command.c_str());
    5584              :    }
    5585              : 
    5586            0 :    return SUCCESS;
    5587            0 : }
    5588              : 
    5589              : /**dox***************************************************************/
    5590              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    5591              : 
    5592              : static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
    5593              : 
    5594              : /********************************************************************/
    5595              : /**
    5596              : Perform midas periodic tasks - check alarms, update and check
    5597              : timeouts on odb and on event buffers, etc. Normally called by cm_yield().
    5598              : Programs that do not use cm_yield(), i.e. the mserver,
    5599              : should call this function periodically, every 1 or 2 seconds.
    5600              : @return CM_SUCCESS
    5601              : */
    5602            0 : INT cm_periodic_tasks() {
    5603              :    static DWORD alarm_last_checked_sec = 0;
    5604            0 :    DWORD now_sec = ss_time();
    5605              : 
    5606            0 :    DWORD now_millitime = ss_millitime();
    5607              :    static DWORD last_millitime = 0;
    5608            0 :    DWORD tdiff_millitime = now_millitime - last_millitime;
    5609            0 :    const DWORD kPeriod = 1000;
    5610            0 :    if (last_millitime == 0) {
    5611            0 :       last_millitime = now_millitime;
    5612            0 :       tdiff_millitime = kPeriod; // make sure first time we come here we do something.
    5613              :    }
    5614              : 
    5615              :    //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
    5616              : 
    5617              :    //if (now_millitime < last_millitime) {
    5618              :    //   printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
    5619              :    //}
    5620              : 
    5621              :    /* check alarms once every 10 seconds */
    5622            0 :    if (now_sec - alarm_last_checked_sec > 10) {
    5623            0 :       al_check();
    5624            0 :       alarm_last_checked_sec = now_sec;
    5625              :    }
    5626              : 
    5627              :    /* run periodic checks previously done by cm_watchdog */
    5628              : 
    5629            0 :    if (tdiff_millitime >= kPeriod) {
    5630            0 :       BOOL wrong_interval = FALSE;
    5631            0 :       if (tdiff_millitime > 60000)
    5632            0 :          wrong_interval = TRUE;
    5633              : 
    5634              :       //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
    5635              : 
    5636            0 :       bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
    5637            0 :       db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
    5638              : 
    5639            0 :       bm_write_statistics_to_odb();
    5640              : 
    5641            0 :       last_millitime = now_millitime;
    5642              :    }
    5643              : 
    5644              :    /* reap transition thread */
    5645              : 
    5646            0 :    cm_transition_cleanup();
    5647              : 
    5648            0 :    return CM_SUCCESS;
    5649              : }
    5650              : 
    5651              : /********************************************************************/
    5652              : /**
    5653              : Central yield functions for clients. This routine should
    5654              : be called in an infinite loop by a client in order to
    5655              : give the MIDAS system the opportunity to receive commands
    5656              : over RPC channels, update database records and receive
    5657              : events.
    5658              : @param millisec         Timeout in millisec. If no message is
    5659              :                         received during the specified timeout,
    5660              :                         the routine returns. If millisec=-1,
    5661              :                         it only returns when receiving an
    5662              :                         RPC_SHUTDOWN message.
    5663              : @return CM_SUCCESS, RPC_SHUTDOWN
    5664              : */
    5665            0 : INT cm_yield(INT millisec) {
    5666              :    INT status;
    5667              :    INT bMore;
    5668              :    //static DWORD last_yield = 0;
    5669              :    //static DWORD last_yield_time = 0;
    5670              :    //DWORD start_yield = ss_millitime();
    5671              : 
    5672              :    /* check for ctrl-c */
    5673            0 :    if (_ctrlc_pressed)
    5674            0 :       return RPC_SHUTDOWN;
    5675              : 
    5676              :    /* flush the cm_msg buffer */
    5677            0 :    cm_msg_flush_buffer();
    5678              : 
    5679            0 :    if (!rpc_is_remote()) {
    5680              :       /* flush the ODB to its binary file */
    5681              :       /* for remote clients, ODB is flushed by the mserver */
    5682              :       HNDLE hDB;
    5683            0 :       cm_get_experiment_database(&hDB, NULL);
    5684            0 :       db_flush_database(hDB);
    5685              :    }
    5686              : 
    5687              :    /* check for available events */
    5688            0 :    if (rpc_is_remote()) {
    5689              :       //printf("cm_yield() calling bm_poll_event()\n");
    5690            0 :       status = bm_poll_event();
    5691              : 
    5692            0 :       if (status == SS_ABORT) {
    5693            0 :          return status;
    5694              :       }
    5695              : 
    5696            0 :       if (status == BM_SUCCESS) {
    5697              :          /* one or more events received by bm_poll_event() */
    5698            0 :          status = ss_suspend(0, 0);
    5699              :       } else {
    5700            0 :          status = ss_suspend(millisec, 0);
    5701              :       }
    5702              : 
    5703            0 :       return status;
    5704              :    }
    5705              : 
    5706            0 :    status = cm_periodic_tasks();
    5707              : 
    5708            0 :    if (status != CM_SUCCESS)
    5709            0 :       return status;
    5710              : 
    5711              :    //DWORD start_check = ss_millitime();
    5712              : 
    5713            0 :    bMore = bm_check_buffers();
    5714              : 
    5715              :    //DWORD end_check = ss_millitime();
    5716              :    //printf("cm_yield: timeout %4d, yield period %4d, last yield time %4d, bm_check_buffers() elapsed %4d, returned %d\n", millisec, start_yield - last_yield, last_yield_time, end_check - start_check, bMore);
    5717              :    //fflush(stdout);
    5718              : 
    5719            0 :    if (bMore == BM_CORRUPTED) {
    5720            0 :       status = SS_ABORT;
    5721            0 :    } else if (bMore) {
    5722              :       /* if events available, quickly check other IPC channels */
    5723            0 :       status = ss_suspend(0, 0);
    5724              :    } else {
    5725            0 :       status = ss_suspend(millisec, 0);
    5726              :    }
    5727              : 
    5728              :    /* flush the cm_msg buffer */
    5729            0 :    cm_msg_flush_buffer();
    5730              : 
    5731              :    //DWORD end_yield = ss_millitime();
    5732              :    //last_yield_time = end_yield - start_yield;
    5733              :    //last_yield = start_yield;
    5734              : 
    5735            0 :    return status;
    5736              : }
    5737              : 
    5738              : /********************************************************************/
    5739              : /**
    5740              : Executes command via system() call
    5741              : @param    command          Command string to execute
    5742              : @param    result           stdout of command
    5743              : @param    bufsize          string size in byte
    5744              : @return   CM_SUCCESS
    5745              : */
    5746            0 : INT cm_execute(const char *command, char *result, INT bufsize) {
    5747              :    char str[256];
    5748              :    INT n;
    5749              :    int fh;
    5750            0 :    int status = 0;
    5751              :    static int check_cm_execute = 1;
    5752              :    static int enable_cm_execute = 0;
    5753              : 
    5754            0 :    if (rpc_is_remote())
    5755            0 :       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
    5756              : 
    5757            0 :    if (check_cm_execute) {
    5758              :       int status;
    5759              :       int size;
    5760              :       HNDLE hDB;
    5761            0 :       check_cm_execute = 0;
    5762              : 
    5763            0 :       status = cm_get_experiment_database(&hDB, NULL);
    5764            0 :       assert(status == DB_SUCCESS);
    5765              : 
    5766            0 :       size = sizeof(enable_cm_execute);
    5767            0 :       status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
    5768            0 :       assert(status == DB_SUCCESS);
    5769              : 
    5770              :       //printf("enable_cm_execute %d\n", enable_cm_execute);
    5771              :    }
    5772              : 
    5773            0 :    if (!enable_cm_execute) {
    5774              :       char buf[32];
    5775            0 :       mstrlcpy(buf, command, sizeof(buf));
    5776            0 :       cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
    5777            0 :       return CM_WRONG_PASSWORD;
    5778              :    }
    5779              : 
    5780            0 :    if (bufsize > 0) {
    5781            0 :       strcpy(str, command);
    5782            0 :       sprintf(str, "%s > %d.tmp", command, ss_getpid());
    5783              : 
    5784            0 :       status = system(str);
    5785              : 
    5786            0 :       sprintf(str, "%d.tmp", ss_getpid());
    5787            0 :       fh = open(str, O_RDONLY, 0644);
    5788            0 :       result[0] = 0;
    5789            0 :       if (fh) {
    5790            0 :          n = read(fh, result, bufsize - 1);
    5791            0 :          result[MAX(0, n)] = 0;
    5792            0 :          close(fh);
    5793              :       }
    5794            0 :       remove(str);
    5795              :    } else {
    5796            0 :       status = system(command);
    5797              :    }
    5798              : 
    5799            0 :    if (status < 0) {
    5800            0 :       cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
    5801            0 :       return CM_SET_ERROR;
    5802              :    }
    5803              : 
    5804            0 :    return CM_SUCCESS;
    5805              : }
    5806              : 
    5807              : 
    5808              : 
    5809              : /**dox***************************************************************/
    5810              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    5811              : 
    5812              : /********************************************************************/
    5813            2 : INT cm_register_function(INT id, INT(*func)(INT, void **))
    5814              : /********************************************************************\
    5815              : 
    5816              :   Routine: cm_register_function
    5817              : 
    5818              :   Purpose: Call rpc_register_function and publish the registered
    5819              :            function under system/clients/<pid>/RPC
    5820              : 
    5821              :   Input:
    5822              :     INT      id             RPC ID
    5823              :     INT      *func          New dispatch function
    5824              : 
    5825              :   Output:
    5826              :    <implicit: func gets copied to rpc_list>
    5827              : 
    5828              :   Function value:
    5829              :    CM_SUCCESS               Successful completion
    5830              :    RPC_INVALID_ID           RPC ID not found
    5831              : 
    5832              : \********************************************************************/
    5833              : {
    5834              :    HNDLE hDB, hKey;
    5835              :    INT status;
    5836              :    char str[80];
    5837              : 
    5838            2 :    status = rpc_register_function(id, func);
    5839            2 :    if (status != RPC_SUCCESS)
    5840            0 :       return status;
    5841              : 
    5842            2 :    cm_get_experiment_database(&hDB, &hKey);
    5843              : 
    5844              :    /* create new key for this id */
    5845            2 :    status = 1;
    5846            2 :    sprintf(str, "RPC/%d", id);
    5847              : 
    5848            2 :    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
    5849            2 :    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
    5850            2 :    db_set_mode(hDB, hKey, MODE_READ, TRUE);
    5851              : 
    5852            2 :    if (status != DB_SUCCESS)
    5853            0 :       return status;
    5854              : 
    5855            2 :    return CM_SUCCESS;
    5856              : }
    5857              : 
    5858              : 
    5859              : /**dox***************************************************************/
    5860              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    5861              : 
    5862              : //
    5863              : // Return "/"-terminated file path for given history channel
    5864              : //
    5865              : 
    5866            0 : std::string cm_get_history_path(const char* history_channel)
    5867              : {
    5868              :    int status;
    5869              :    HNDLE hDB;
    5870            0 :    std::string path;
    5871              : 
    5872            0 :    cm_get_experiment_database(&hDB, NULL);
    5873              : 
    5874            0 :    if (history_channel && (strlen(history_channel) > 0)) {
    5875            0 :       std::string p;
    5876            0 :       p += "/Logger/History/";
    5877            0 :       p += history_channel;
    5878            0 :       p += "/History dir";
    5879              : 
    5880              :       // NB: be careful to avoid creating odb entries under /logger
    5881              :       // for whatever values of "history_channel" we get called with!
    5882            0 :       status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
    5883            0 :       if (status == DB_SUCCESS && path.length() > 0) {
    5884              :          // if not absolute path, prepend with experiment directory
    5885            0 :          if (path[0] != DIR_SEPARATOR)
    5886            0 :             path = cm_get_path() + path;
    5887              :          // append directory separator
    5888            0 :          if (path.back() != DIR_SEPARATOR)
    5889            0 :             path += DIR_SEPARATOR_STR;
    5890              :          //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
    5891            0 :          return path;
    5892              :       }
    5893            0 :    }
    5894              : 
    5895            0 :    status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
    5896            0 :    if (status == DB_SUCCESS && path.length() > 0) {
    5897              :       // if not absolute path, prepend with experiment directory
    5898            0 :       if (path[0] != DIR_SEPARATOR)
    5899            0 :          path = cm_get_path() + path;
    5900              :       // append directory separator
    5901            0 :       if (path.back() != DIR_SEPARATOR)
    5902            0 :          path += DIR_SEPARATOR_STR;
    5903              :       //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
    5904            0 :       return path;
    5905              :    }
    5906              : 
    5907            0 :    status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
    5908            0 :    if (status == DB_SUCCESS && path.length() > 0) {
    5909              :       // if not absolute path, prepend with experiment directory
    5910            0 :       if (path[0] != DIR_SEPARATOR)
    5911            0 :          path = cm_get_path() + path;
    5912              :       // append directory separator
    5913            0 :       if (path.back() != DIR_SEPARATOR)
    5914            0 :          path += DIR_SEPARATOR_STR;
    5915              :       //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
    5916            0 :       return path;
    5917              :    }
    5918              : 
    5919              :    //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
    5920            0 :    return cm_get_path();
    5921            0 : }
    5922              : 
    5923              : /**dox***************************************************************/
    5924              : /** @} *//* end of cmfunctionc */
    5925              : 
    5926              : /**dox***************************************************************/
    5927              : /** @addtogroup bmfunctionc
    5928              :  *
    5929              :  *  @{  */
    5930              : 
    5931              : /********************************************************************\
    5932              : *                                                                    *
    5933              : *                 bm_xxx  -  Buffer Manager Functions                *
    5934              : *                                                                    *
    5935              : \********************************************************************/
    5936              : 
    5937              : static DWORD _bm_max_event_size = 0;
    5938              : 
    5939              : #ifdef LOCAL_ROUTINES
    5940              : 
    5941              : // see locking code in xbm_lock_buffer()
    5942              : static int _bm_lock_timeout = 5 * 60 * 1000;
    5943              : static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
    5944              : 
    5945            6 : static int bm_validate_client_index_locked(bm_lock_buffer_guard& pbuf_guard)
    5946              : {
    5947            6 :    const BUFFER *pbuf = pbuf_guard.get_pbuf();
    5948              : 
    5949            6 :    bool badindex  = false;
    5950            6 :    bool badclient = false;
    5951              : 
    5952            6 :    int idx = pbuf->client_index;
    5953              : 
    5954            6 :    if (idx < 0) {
    5955            0 :       badindex = true;
    5956            6 :    } else if (idx > pbuf->buffer_header->max_client_index) {
    5957            0 :       badindex = true;
    5958              :    } else {
    5959            6 :       BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
    5960            6 :       if (pclient->name[0] == 0)
    5961            0 :          badclient = true;
    5962            6 :       else if (pclient->pid != ss_getpid())
    5963            0 :          badclient = true;
    5964              : 
    5965              :       //if (strcmp(pclient->name,"mdump")==0) {
    5966              :       //   for (int i=0; i<15; i++) {
    5967              :       //      printf("sleep %d\n", i);
    5968              :       //      ::sleep(1);
    5969              :       //   }
    5970              :       //}
    5971              :    }
    5972              : 
    5973              : #if 0
    5974              :    if (badindex) {
    5975              :       printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
    5976              :              pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
    5977              :              badindex, ss_getpid());
    5978              :    } else if (badclient) {
    5979              :       printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, client_name=\'%s\', client_pid=%d, pid=%d, badclient %d\n",
    5980              :              pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
    5981              :              pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
    5982              :              ss_getpid(), badclient);
    5983              :    } else {
    5984              :       printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, client_name=\'%s\', client_pid=%d, pid=%d, goodclient\n",
    5985              :              pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
    5986              :              pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
    5987              :              ss_getpid());
    5988              :    }
    5989              : #endif
    5990              : 
    5991            6 :    if (badindex || badclient) {
    5992              :       static int prevent_recursion = 1;
    5993              : 
    5994            0 :       if (prevent_recursion) {
    5995            0 :          prevent_recursion = 0;
    5996              : 
    5997            0 :          if (badindex) {
    5998            0 :             cm_msg(MERROR, "bm_validate_client_index", "My client index %d in buffer \'%s\' is invalid, max_client_index %d, my pid %d", idx, pbuf->buffer_header->name, pbuf->buffer_header->max_client_index, ss_getpid());
    5999              :          } else {
    6000            0 :             cm_msg(MERROR, "bm_validate_client_index", "My client index %d in buffer \'%s\' is invalid: client name \'%s\', pid %d should be my pid %d", idx, pbuf->buffer_header->name, pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid, ss_getpid());
    6001              :          }
    6002              : 
    6003            0 :          cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
    6004              :       }
    6005              : 
    6006            0 :       if (badindex) {
    6007            0 :          fprintf(stderr, "bm_validate_client_index: My client index %d in buffer \'%s\' is invalid, max_client_index %d, my pid %d\n", idx, pbuf->buffer_header->name, pbuf->buffer_header->max_client_index, ss_getpid());
    6008              :       } else {
    6009            0 :          fprintf(stderr, "bm_validate_client_index: My client index %d in buffer \'%s\' is invalid: client name \'%s\', pid %d should be my pid %d\n", idx, pbuf->buffer_header->name, pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid, ss_getpid());
    6010              :       }
    6011              :       
    6012            0 :       fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
    6013              : 
    6014            0 :       pbuf_guard.unlock();
    6015              : 
    6016            0 :       abort();
    6017              :    }
    6018              : 
    6019            6 :    return idx;
    6020              : }
    6021              : 
    6022            6 : static BUFFER_CLIENT *bm_get_my_client_locked(bm_lock_buffer_guard& pbuf_guard) {
    6023            6 :    int my_client_index = bm_validate_client_index_locked(pbuf_guard);
    6024            6 :    return pbuf_guard.get_pbuf()->buffer_header->client + my_client_index;
    6025              : }
    6026              : 
    6027              : #endif // LOCAL_ROUTINES
    6028              : 
    6029              : /********************************************************************/
    6030              : /**
    6031              : Check if an event matches a given event request by the
    6032              : event id and trigger mask
    6033              : @param event_id      Event ID of request
    6034              : @param trigger_mask  Trigger mask of request
    6035              : @param pevent    Pointer to event to check
    6036              : @return TRUE      if event matches request
    6037              : */
    6038            0 : INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
    6039              :    // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
    6040              :    // because of mismatch in sign-extension between signed 16-bit event_id and
    6041              :    // unsigned 16-bit constants. K.O.
    6042              : 
    6043            0 :    if (((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) || ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG)))
    6044              :       /* fragmented event */
    6045            0 :       return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
    6046            0 :               && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
    6047              : 
    6048            0 :    return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
    6049            0 :            && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
    6050              : }
    6051              : 
    6052              : #ifdef LOCAL_ROUTINES
    6053              : 
    6054              : /********************************************************************/
    6055              : /**
    6056              : Called to forcibly disconnect given client from a data buffer
    6057              : */
    6058            0 : void bm_remove_client_locked(BUFFER_HEADER *pheader, int j) {
    6059              :    int k, nc;
    6060              :    BUFFER_CLIENT *pbctmp;
    6061              : 
    6062              :    /* clear entry from client structure in buffer header */
    6063            0 :    memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
    6064              : 
    6065              :    /* calculate new max_client_index entry */
    6066            0 :    for (k = MAX_CLIENTS - 1; k >= 0; k--)
    6067            0 :       if (pheader->client[k].pid != 0)
    6068            0 :          break;
    6069            0 :    pheader->max_client_index = k + 1;
    6070              : 
    6071              :    /* count new number of clients */
    6072            0 :    for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
    6073            0 :       if (pheader->client[k].pid != 0)
    6074            0 :          nc++;
    6075            0 :    pheader->num_clients = nc;
    6076              : 
    6077              :    /* check if anyone is waiting and wake him up */
    6078            0 :    pbctmp = pheader->client;
    6079              : 
    6080            0 :    for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
    6081            0 :       if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
    6082            0 :          ss_resume(pbctmp->port, "B  ");
    6083            0 : }
    6084              : 
    6085              : /********************************************************************/
    6086              : /**
    6087              : Check all clients on buffer, remove invalid clients
    6088              : */
    6089            4 : static void bm_cleanup_buffer_locked(BUFFER* pbuf, const char *who, DWORD actual_time) {
    6090              :    BUFFER_HEADER *pheader;
    6091              :    BUFFER_CLIENT *pbclient;
    6092              :    int j;
    6093              : 
    6094            4 :    pheader = pbuf->buffer_header;
    6095            4 :    pbclient = pheader->client;
    6096              : 
    6097              :    /* now check other clients */
    6098            6 :    for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
    6099            2 :       if (pbclient->pid) {
    6100            2 :          if (!ss_pid_exists(pbclient->pid)) {
    6101            0 :             cm_msg(MINFO, "bm_cleanup",
    6102            0 :                    "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
    6103            0 :                    pheader->name, who, pbclient->pid);
    6104              : 
    6105            0 :             bm_remove_client_locked(pheader, j);
    6106            0 :             continue;
    6107              :          }
    6108              :       }
    6109              : 
    6110              :       /* If client process has no activity, clear its buffer entry. */
    6111            2 :       if (pbclient->pid && pbclient->watchdog_timeout > 0) {
    6112            2 :          DWORD tdiff = actual_time - pbclient->last_activity;
    6113              : #if 0
    6114              :          printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
    6115              :                 pheader->name,
    6116              :                 pbclient->name,
    6117              :                 pbclient->last_activity,
    6118              :                 actual_time,
    6119              :                 tdiff,
    6120              :                 tdiff,
    6121              :                 pbclient->watchdog_timeout);
    6122              : #endif
    6123            2 :          if (actual_time > pbclient->last_activity &&
    6124            0 :              tdiff > pbclient->watchdog_timeout) {
    6125              : 
    6126            0 :             cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
    6127            0 :                    pbclient->name, pheader->name, who,
    6128              :                    tdiff / 1000.0,
    6129            0 :                    pbclient->watchdog_timeout / 1000.0);
    6130              : 
    6131            0 :             bm_remove_client_locked(pheader, j);
    6132              :          }
    6133              :       }
    6134              :    }
    6135            4 : }
    6136              : 
    6137              : /**
    6138              : Update last activity time
    6139              : */
    6140            0 : static void bm_update_last_activity(DWORD millitime) {
    6141            0 :    int pid = ss_getpid();
    6142              : 
    6143            0 :    std::vector<BUFFER*> mybuffers;
    6144              : 
    6145            0 :    gBuffersMutex.lock();
    6146            0 :    mybuffers = gBuffers;
    6147            0 :    gBuffersMutex.unlock();
    6148              : 
    6149            0 :    for (BUFFER* pbuf : mybuffers) {
    6150            0 :       if (!pbuf)
    6151            0 :          continue;
    6152            0 :       if (pbuf->attached) {
    6153              : 
    6154            0 :          bm_lock_buffer_guard pbuf_guard(pbuf);
    6155              : 
    6156            0 :          if (!pbuf_guard.is_locked())
    6157            0 :             continue;
    6158              : 
    6159            0 :          BUFFER_HEADER *pheader = pbuf->buffer_header;
    6160            0 :          for (int j = 0; j < pheader->max_client_index; j++) {
    6161            0 :             BUFFER_CLIENT *pclient = pheader->client + j;
    6162            0 :             if (pclient->pid == pid) {
    6163            0 :                pclient->last_activity = millitime;
    6164              :             }
    6165              :          }
    6166            0 :       }
    6167              :    }
    6168            0 : }
    6169              : 
    6170              : #endif // LOCAL_ROUTINES
    6171              : 
    6172              : /**
    6173              : Check all clients on all buffers, remove invalid clients
    6174              : */
    6175            4 : static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
    6176              : {
    6177              : #ifdef LOCAL_ROUTINES
    6178              : 
    6179              :    //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
    6180              : 
    6181            4 :    std::vector<BUFFER*> mybuffers;
    6182              : 
    6183            4 :    gBuffersMutex.lock();
    6184            4 :    mybuffers = gBuffers;
    6185            4 :    gBuffersMutex.unlock();
    6186              : 
    6187              :    /* check buffers */
    6188            6 :    for (BUFFER* pbuf : mybuffers) {
    6189            2 :       if (!pbuf)
    6190            0 :          continue;
    6191            2 :       if (pbuf->attached) {
    6192              :          /* update the last_activity entry to show that we are alive */
    6193              : 
    6194            2 :          bm_lock_buffer_guard pbuf_guard(pbuf);
    6195              :          
    6196            2 :          if (!pbuf_guard.is_locked())
    6197            0 :             continue;
    6198              : 
    6199            2 :          BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
    6200            2 :          pclient->last_activity = actual_time;
    6201              : 
    6202              :          /* don't check other clients if interval is strange */
    6203            2 :          if (!wrong_interval)
    6204            2 :             bm_cleanup_buffer_locked(pbuf, who, actual_time);
    6205            2 :       }
    6206              :    }
    6207              : #endif // LOCAL_ROUTINES
    6208            4 : }
    6209              : 
    6210              : #ifdef LOCAL_ROUTINES
    6211              : 
    6212            2 : static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
    6213            2 :    if (rp < 0 || rp > pheader->size) {
    6214            0 :       cm_msg(MERROR, "bm_validate_rp",
    6215              :              "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
    6216            0 :              pheader->name,
    6217              :              rp,
    6218            0 :              pheader->read_pointer,
    6219            0 :              pheader->write_pointer,
    6220            0 :              pheader->size,
    6221              :              who);
    6222            0 :       return FALSE;
    6223              :    }
    6224              : 
    6225            2 :    if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
    6226              :       // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
    6227            0 :       cm_msg(MERROR, "bm_validate_rp",
    6228              :              "error: buffer \"%s\" is corrupted: rp %d plus event header point beyond the end of buffer by %d bytes. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
    6229            0 :              pheader->name,
    6230              :              rp,
    6231            0 :              (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
    6232            0 :              pheader->read_pointer,
    6233            0 :              pheader->write_pointer,
    6234            0 :              pheader->size,
    6235              :              who);
    6236            0 :       return FALSE;
    6237              :    }
    6238              : 
    6239            2 :    return TRUE;
    6240              : }
    6241              : 
    6242              : #if 0
    6243              : static FILE* gRpLog = NULL;
    6244              : #endif
    6245              : 
    6246            0 : static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
    6247              : {
    6248              : #if 0
    6249              :    if (gRpLog == NULL) {
    6250              :       gRpLog = fopen("rp.log", "a");
    6251              :    }
    6252              :    if (gRpLog && (total_size < 16)) {
    6253              :       const char *pdata = (const char *) (pheader + 1);
    6254              :       const DWORD *pevent = (const DWORD*) (pdata + rp);
    6255              :       fprintf(gRpLog, "%s: rp %d, total_size %d, at rp 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", pheader->name, rp, total_size,
    6256              :               pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
    6257              :    }
    6258              : #endif
    6259              : 
    6260              :    // these checks are already done before we come here.
    6261              :    // but we check again as last-ressort protection. K.O.
    6262            0 :    assert(total_size > 0);
    6263            0 :    assert(total_size >= (int)sizeof(EVENT_HEADER));
    6264              : 
    6265            0 :    rp += total_size;
    6266            0 :    if (rp >= pheader->size) {
    6267            0 :       rp -= pheader->size;
    6268            0 :    } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
    6269              :       // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
    6270              :       // if at the end of the buffer, the remaining free space is exactly
    6271              :       // equal to the size of an event header, the event header
    6272              :       // is written there, the pointer is wrapped and the event data
    6273              :       // is written to the beginning of the buffer.
    6274            0 :       rp = 0;
    6275              :    }
    6276            0 :    return rp;
    6277              : }
    6278              : 
    6279            0 : static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
    6280            0 :    const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
    6281            0 :    int event_size = pevent->data_size + sizeof(EVENT_HEADER);
    6282            0 :    int total_size = ALIGN8(event_size);
    6283              : 
    6284            0 :    if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
    6285            0 :       cm_msg(MERROR, "bm_next_rp",
    6286              :              "error: buffer \"%s\" is corrupted: rp %d points to an invalid event: data_size %d, event size %d, total_size %d, buffer read_pointer %d, write_pointer %d, size %d, called from %s",
    6287            0 :              pheader->name,
    6288              :              rp,
    6289            0 :              pevent->data_size,
    6290              :              event_size,
    6291              :              total_size,
    6292            0 :              pheader->read_pointer,
    6293            0 :              pheader->write_pointer,
    6294            0 :              pheader->size,
    6295              :              who);
    6296            0 :       return -1;
    6297              :    }
    6298              : 
    6299            0 :    int remaining = 0;
    6300            0 :    if (rp < pheader->write_pointer) {
    6301            0 :       remaining = pheader->write_pointer - rp;
    6302              :    } else {
    6303            0 :       remaining = pheader->size - rp;
    6304            0 :       remaining += pheader->write_pointer;
    6305              :    }
    6306              : 
    6307              :    //printf("bm_next_rp: total_size %d, remaining %d, rp %d, wp %d, size %d\n", total_size, remaining, rp, pheader->write_pointer, pheader->size);
    6308              : 
    6309            0 :    if (total_size > remaining) {
    6310            0 :       cm_msg(MERROR, "bm_next_rp",
    6311              :              "error: buffer \"%s\" is corrupted: rp %d points to an invalid event: data_size %d, event size %d, total_size %d, buffer read_pointer %d, write_pointer %d, size %d, remaining %d, called from %s",
    6312            0 :              pheader->name,
    6313              :              rp,
    6314            0 :              pevent->data_size,
    6315              :              event_size,
    6316              :              total_size,
    6317            0 :              pheader->read_pointer,
    6318            0 :              pheader->write_pointer,
    6319            0 :              pheader->size,
    6320              :              remaining,
    6321              :              who);
    6322            0 :       return -1;
    6323              :    }
    6324              : 
    6325            0 :    rp = bm_incr_rp_no_check(pheader, rp, total_size);
    6326              : 
    6327            0 :    return rp;
    6328              : }
    6329              : 
    6330            2 : static int bm_validate_buffer_locked(const BUFFER *pbuf) {
    6331            2 :    const BUFFER_HEADER *pheader = pbuf->buffer_header;
    6332            2 :    const char *pdata = (const char *) (pheader + 1);
    6333              : 
    6334              :    //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
    6335              : 
    6336              :    //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
    6337              : 
    6338              :    //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
    6339              : 
    6340            2 :    if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
    6341            0 :       cm_msg(MERROR, "bm_validate_buffer",
    6342            0 :              "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
    6343            0 :              pheader->read_pointer, pheader->size, pheader->write_pointer);
    6344            0 :       return BM_CORRUPTED;
    6345              :    }
    6346              : 
    6347            2 :    if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
    6348            0 :       cm_msg(MERROR, "bm_validate_buffer",
    6349            0 :              "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
    6350            0 :              pheader->write_pointer, pheader->size, pheader->read_pointer);
    6351            0 :       return BM_CORRUPTED;
    6352              :    }
    6353              : 
    6354            2 :    if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
    6355            0 :       cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
    6356            0 :              pheader->read_pointer);
    6357            0 :       return BM_CORRUPTED;
    6358              :    }
    6359              : 
    6360            2 :    int rp = pheader->read_pointer;
    6361            2 :    int rp0 = -1;
    6362            2 :    while (rp != pheader->write_pointer) {
    6363            0 :       if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
    6364            0 :          cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
    6365            0 :                 pheader->name, rp, rp0);
    6366            0 :          return BM_CORRUPTED;
    6367              :       }
    6368              :       //bm_print_event(pdata, rp);
    6369            0 :       int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
    6370            0 :       if (rp1 < 0) {
    6371            0 :          cm_msg(MERROR, "bm_validate_buffer",
    6372            0 :                 "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
    6373            0 :          return BM_CORRUPTED;
    6374              :       }
    6375            0 :       rp0 = rp;
    6376            0 :       rp = rp1;
    6377              :    }
    6378              : 
    6379              :    int i;
    6380          130 :    for (i = 0; i < MAX_CLIENTS; i++) {
    6381          128 :       const BUFFER_CLIENT *c = &pheader->client[i];
    6382          128 :       if (c->pid == 0)
    6383          128 :          continue;
    6384            0 :       BOOL get_all = FALSE;
    6385              :       int j;
    6386            0 :       for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
    6387            0 :          const EVENT_REQUEST *r = &c->event_request[j];
    6388            0 :          if (!r->valid)
    6389            0 :             continue;
    6390            0 :          BOOL xget_all = r->sampling_type == GET_ALL;
    6391            0 :          get_all = (get_all || xget_all);
    6392              :          //printf("client slot %d: pid %d, name \"%s\", request %d: id %d, valid %d, sampling_type %d, get_all %d\n", i, c->pid, c->name, j, r->id, r->valid, r->sampling_type, xget_all);
    6393              :       }
    6394              : 
    6395            0 :       int rp = c->read_pointer;
    6396            0 :       int rp0 = -1;
    6397            0 :       while (rp != pheader->write_pointer) {
    6398              :          //bm_print_event(pdata, rp);
    6399            0 :          int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
    6400            0 :          if (rp1 < 0) {
    6401            0 :             cm_msg(MERROR, "bm_validate_buffer",
    6402              :                    "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
    6403            0 :                    pheader->name, c->name, c->read_pointer, rp, rp0);
    6404            0 :             return BM_CORRUPTED;
    6405              :          }
    6406            0 :          rp0 = rp;
    6407            0 :          rp = rp1;
    6408              :       }
    6409              :    }
    6410              : 
    6411            2 :    return BM_SUCCESS;
    6412              : }
    6413              : 
    6414            0 : static void bm_reset_buffer_locked(BUFFER *pbuf) {
    6415            0 :    BUFFER_HEADER *pheader = pbuf->buffer_header;
    6416              : 
    6417              :    //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
    6418              : 
    6419            0 :    pheader->read_pointer = 0;
    6420            0 :    pheader->write_pointer = 0;
    6421              : 
    6422              :    int i;
    6423            0 :    for (i = 0; i < pheader->max_client_index; i++) {
    6424            0 :       BUFFER_CLIENT *pc = pheader->client + i;
    6425            0 :       if (pc->pid) {
    6426            0 :          pc->read_pointer = 0;
    6427              :       }
    6428              :    }
    6429            0 : }
    6430              : 
    6431            2 : static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf) {
    6432              :    HNDLE hKey;
    6433              :    int status;
    6434              : 
    6435              :    char str[256 + 2 * NAME_LENGTH];
    6436            2 :    sprintf(str, "/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
    6437              :    //printf("delete [%s]\n", str);
    6438            2 :    status = db_find_key(hDB, 0, str, &hKey);
    6439            2 :    if (status == DB_SUCCESS) {
    6440            0 :       status = db_delete_key(hDB, hKey, FALSE);
    6441              :    }
    6442            2 : }
    6443              : 
    6444              : struct BUFFER_INFO
    6445              : {
    6446              :    BOOL get_all_flag = false;         /**< this is a get_all reader     */
    6447              : 
    6448              :    /* buffer statistics */
    6449              :    int count_lock = 0;                /**< count how many times we locked the buffer */
    6450              :    int count_sent = 0;                /**< count how many events we sent */
    6451              :    double bytes_sent = 0;             /**< count how many bytes we sent */
    6452              :    int count_write_wait = 0;          /**< count how many times we waited for free space */
    6453              :    DWORD time_write_wait = 0;         /**< count for how long we waited for free space, in units of ss_millitime() */
    6454              :    int last_count_lock = 0;           /**< avoid writing statistics to odb if lock count did not change */
    6455              :    DWORD wait_start_time = 0;         /**< time when we started the wait */
    6456              :    int wait_client_index = 0;         /**< waiting for which client */
    6457              :    int max_requested_space = 0;       /**< waiting for this many bytes of free space */
    6458              :    int count_read = 0;                /**< count how many events we read */
    6459              :    double bytes_read = 0;             /**< count how many bytes we read */
    6460              :    int client_count_write_wait[MAX_CLIENTS]; /**< per-client count_write_wait */
    6461              :    DWORD client_time_write_wait[MAX_CLIENTS]; /**< per-client time_write_wait */
    6462              : 
    6463            4 :    BUFFER_INFO(BUFFER* pbuf)
    6464            4 :    {
    6465            4 :       get_all_flag = pbuf->get_all_flag;
    6466              : 
    6467              :       /* buffer statistics */
    6468            4 :       count_lock        = pbuf->count_lock;
    6469            4 :       count_sent        = pbuf->count_sent;
    6470            4 :       bytes_sent        = pbuf->bytes_sent;
    6471            4 :       count_write_wait  = pbuf->count_write_wait;
    6472            4 :       time_write_wait   = pbuf->time_write_wait;
    6473            4 :       last_count_lock   = pbuf->last_count_lock;
    6474            4 :       wait_start_time   = pbuf->wait_start_time;
    6475            4 :       wait_client_index = pbuf->wait_client_index;
    6476            4 :       max_requested_space = pbuf->max_requested_space;
    6477            4 :       count_read        = pbuf->count_read;
    6478            4 :       bytes_read        = pbuf->bytes_read;
    6479              : 
    6480          260 :       for (int i=0; i<MAX_CLIENTS; i++) {
    6481          256 :          client_count_write_wait[i] = pbuf->client_count_write_wait[i];
    6482          256 :          client_time_write_wait[i] = pbuf->client_time_write_wait[i];
    6483              :       }
    6484            4 :    };
    6485              : };
    6486              : 
    6487            4 : static void bm_write_buffer_statistics_to_odb_copy(HNDLE hDB, const char* buffer_name, const char* client_name, int client_index, BUFFER_INFO *pbuf, BUFFER_HEADER* pheader)
    6488              : {
    6489              :    int status;
    6490              : 
    6491            4 :    DWORD now = ss_millitime();
    6492              : 
    6493              :    HNDLE hKey;
    6494            4 :    status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
    6495            4 :    if (status != DB_SUCCESS) {
    6496            1 :       db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
    6497            1 :       status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
    6498            1 :       if (status != DB_SUCCESS)
    6499            0 :          return;
    6500              :    }
    6501              : 
    6502              :    HNDLE hKeyBuffer;
    6503            4 :    status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
    6504            4 :    if (status != DB_SUCCESS) {
    6505            1 :       db_create_key(hDB, hKey, buffer_name, TID_KEY);
    6506            1 :       status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
    6507            1 :       if (status != DB_SUCCESS)
    6508            0 :          return;
    6509              :    }
    6510              : 
    6511            4 :    double buf_size = pheader->size;
    6512            4 :    double buf_rptr = pheader->read_pointer;
    6513            4 :    double buf_wptr = pheader->write_pointer;
    6514              : 
    6515            4 :    double buf_fill = 0;
    6516            4 :    double buf_cptr = 0;
    6517            4 :    double buf_cused = 0;
    6518            4 :    double buf_cused_pct = 0;
    6519              : 
    6520            4 :    if (client_index >= 0 && client_index <= pheader->max_client_index) {
    6521            4 :       buf_cptr = pheader->client[client_index].read_pointer;
    6522              : 
    6523            4 :       if (buf_wptr == buf_cptr) {
    6524            3 :          buf_cused = 0;
    6525            1 :       } else if (buf_wptr > buf_cptr) {
    6526            1 :          buf_cused = buf_wptr - buf_cptr;
    6527              :       } else {
    6528            0 :          buf_cused = (buf_size - buf_cptr) + buf_wptr;
    6529              :       }
    6530              :       
    6531            4 :       buf_cused_pct = buf_cused / buf_size * 100.0;
    6532              :       
    6533              :       // we cannot write buf_cused and buf_cused_pct into the buffer statistics
    6534              :       // because some other GET_ALL client may have different buf_cused & etc,
    6535              :       // so they must be written into the per-client statistics
    6536              :       // and the web page should look at all the GET_ALL clients and used
    6537              :       // the biggest buf_cused as the whole-buffer "bytes used" value.
    6538              :    }
    6539              : 
    6540            4 :    if (buf_wptr == buf_rptr) {
    6541            3 :       buf_fill = 0;
    6542            1 :    } else if (buf_wptr > buf_rptr) {
    6543            1 :       buf_fill = buf_wptr - buf_rptr;
    6544              :    } else {
    6545            0 :       buf_fill = (buf_size - buf_rptr) + buf_wptr;
    6546              :    }
    6547              : 
    6548            4 :    double buf_fill_pct = buf_fill / buf_size * 100.0;
    6549              : 
    6550            4 :    db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
    6551            4 :    db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
    6552            4 :    db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
    6553            4 :    db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
    6554            4 :    db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
    6555              : 
    6556            4 :    status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
    6557            4 :    if (status != DB_SUCCESS) {
    6558            1 :       db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
    6559            1 :       status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
    6560            1 :       if (status != DB_SUCCESS)
    6561            0 :          return;
    6562              :    }
    6563              : 
    6564              :    HNDLE hKeyClient;
    6565            4 :    status = db_find_key(hDB, hKey, client_name, &hKeyClient);
    6566            4 :    if (status != DB_SUCCESS) {
    6567            2 :       db_create_key(hDB, hKey, client_name, TID_KEY);
    6568            2 :       status = db_find_key(hDB, hKey, client_name, &hKeyClient);
    6569            2 :       if (status != DB_SUCCESS)
    6570            0 :          return;
    6571              :    }
    6572              : 
    6573            4 :    db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
    6574            4 :    db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
    6575            4 :    db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
    6576            4 :    db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
    6577            4 :    db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
    6578            4 :    db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
    6579            4 :    db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
    6580            4 :    db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
    6581            4 :    db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
    6582            4 :    db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
    6583            4 :    db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
    6584            4 :    db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
    6585              : 
    6586          260 :    for (int i = 0; i < MAX_CLIENTS; i++) {
    6587          256 :       if (!pbuf->client_count_write_wait[i])
    6588          256 :          continue;
    6589              :       
    6590            0 :       if (pheader->client[i].pid == 0)
    6591            0 :          continue;
    6592              :       
    6593            0 :       if (pheader->client[i].name[0] == 0)
    6594            0 :          continue;
    6595              :       
    6596              :       char str[100 + NAME_LENGTH];
    6597              :       
    6598            0 :       sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
    6599            0 :       db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
    6600              :       
    6601            0 :       sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
    6602            0 :       db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
    6603              :    }
    6604              : 
    6605            4 :    db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
    6606            4 :    db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
    6607              : }
    6608              : 
    6609            4 : static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
    6610              : {
    6611              :    //printf("bm_buffer_write_statistics_to_odb: buffer [%s] client [%s], lock count %d -> %d, force %d\n", pbuf->buffer_name, pbuf->client_name, pbuf->last_count_lock, pbuf->count_lock, force);
    6612              : 
    6613            4 :    bm_lock_buffer_guard pbuf_guard(pbuf);
    6614              : 
    6615            4 :    if (!pbuf_guard.is_locked())
    6616            0 :       return;
    6617              : 
    6618            4 :    if (!force) {
    6619            0 :       if (pbuf->count_lock == pbuf->last_count_lock) {
    6620            0 :          return;
    6621              :       }
    6622              :    }
    6623              : 
    6624            8 :    std::string buffer_name = pbuf->buffer_name;
    6625            4 :    std::string client_name = pbuf->client_name;
    6626              : 
    6627            4 :    if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
    6628              :       // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
    6629            0 :       pbuf_guard.unlock(); // unlock before cm_msg()
    6630            0 :       cm_msg(MERROR, "bm_write_buffer_statistics_to_odb", "Invalid empty buffer name \"%s\" or client name \"%s\"", buffer_name.c_str(), client_name.c_str());
    6631            0 :       return;
    6632              :    }
    6633              : 
    6634            4 :    pbuf->last_count_lock = pbuf->count_lock;
    6635              : 
    6636            4 :    BUFFER_INFO xbuf(pbuf);
    6637            4 :    BUFFER_HEADER xheader = *pbuf->buffer_header;
    6638            4 :    int client_index = pbuf->client_index;
    6639              : 
    6640            4 :    pbuf_guard.unlock();
    6641              : 
    6642            4 :    bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
    6643            4 : }
    6644              : 
    6645           17 : static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
    6646              : {
    6647           17 :    size_t sbuffer_handle = buffer_handle;
    6648              : 
    6649           17 :    size_t  nbuf = 0;
    6650           17 :    BUFFER* pbuf = NULL;
    6651              : 
    6652           17 :    gBuffersMutex.lock();
    6653              : 
    6654           17 :    nbuf = gBuffers.size();
    6655           17 :    if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
    6656           17 :       pbuf = gBuffers[buffer_handle-1];
    6657              :    }
    6658              : 
    6659           17 :    gBuffersMutex.unlock();
    6660              : 
    6661           17 :    if (sbuffer_handle > nbuf || buffer_handle <= 0) {
    6662            0 :       if (who)
    6663            0 :          cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
    6664            0 :       if (pstatus)
    6665            0 :          *pstatus = BM_INVALID_HANDLE;
    6666            0 :       return NULL;
    6667              :    }
    6668              :    
    6669           17 :    if (!pbuf) {
    6670            0 :       if (who)
    6671            0 :          cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
    6672            0 :       if (pstatus)
    6673            0 :          *pstatus = BM_INVALID_HANDLE;
    6674            0 :       return NULL;
    6675              :    }
    6676              :    
    6677           17 :    if (!pbuf->attached) {
    6678            2 :       if (who)
    6679            0 :          cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
    6680            2 :       if (pstatus)
    6681            2 :          *pstatus = BM_INVALID_HANDLE;
    6682            2 :       return NULL;
    6683              :    }
    6684              :    
    6685           15 :    if (pstatus)
    6686           15 :       *pstatus = BM_SUCCESS;
    6687              : 
    6688           15 :    return pbuf;
    6689              : }
    6690              : 
    6691              : #endif // LOCAL_ROUTINES
    6692              : 
    6693              : /********************************************************************/
    6694              : /**
    6695              : Open an event buffer.
    6696              : Two default buffers are created by the system.
    6697              : The "SYSTEM" buffer is used to
    6698              : exchange events and the "SYSMSG" buffer is used to exchange system messages.
    6699              : The name and size of the event buffers is defined in midas.h as
    6700              : EVENT_BUFFER_NAME and DEFAULT_BUFFER_SIZE.
    6701              : Following example opens the "SYSTEM" buffer, requests events with ID 1 and
    6702              : enters a main loop. Events are then received in process_event()
    6703              : \code
    6704              : #include <stdio.h>
    6705              : #include "midas.h"
    6706              : void process_event(HNDLE hbuf, HNDLE request_id, EVENT_HEADER *pheader, void *pevent)
    6707              : {
    6708              :   printf("Received event #%d\r",
    6709              :   pheader->serial_number);
    6710              : }
    6711              : main()
    6712              : {
    6713              :   INT status, request_id;
    6714              :   HNDLE hbuf;
    6715              :   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
    6716              :   if (status != CM_SUCCESS)
    6717              :   return 1;
    6718              :   bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
    6719              :   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
    6720              : 
    6721              :   do
    6722              :   {
    6723              :    status = cm_yield(1000);
    6724              :   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
    6725              :   cm_disconnect_experiment();
    6726              :   return 0;
    6727              : }
    6728              : \endcode
    6729              : @param buffer_name Name of buffer
    6730              : @param buffer_size Default size of buffer in bytes. Can by overwritten with ODB value
    6731              : @param buffer_handle Buffer handle returned by function
    6732              : @return BM_SUCCESS, BM_CREATED <br>
    6733              : BM_NO_SHM Shared memory cannot be created <br>
    6734              : BM_NO_SEMAPHORE Semaphore cannot be created <br>
    6735              : BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
    6736              : BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
    6737              : different size <br>
    6738              : BM_INVALID_PARAM Invalid parameter
    6739              : */
    6740            2 : INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
    6741              :    INT status;
    6742              : 
    6743            2 :    if (rpc_is_remote()) {
    6744            0 :       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
    6745              : 
    6746              :       HNDLE hDB;
    6747            0 :       status = cm_get_experiment_database(&hDB, NULL);
    6748            0 :       if (status != SUCCESS || hDB == 0) {
    6749            0 :          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
    6750            0 :          return BM_NO_SHM;
    6751              :       }
    6752              : 
    6753            0 :       _bm_max_event_size = DEFAULT_MAX_EVENT_SIZE;
    6754              : 
    6755            0 :       int size = sizeof(INT);
    6756            0 :       status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
    6757              : 
    6758            0 :       if (status != DB_SUCCESS) {
    6759            0 :          cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
    6760              :                 status);
    6761            0 :          return status;
    6762              :       }
    6763              : 
    6764            0 :       return status;
    6765              :    }
    6766              : #ifdef LOCAL_ROUTINES
    6767              :    {
    6768              :       HNDLE shm_handle;
    6769              :       size_t shm_size;
    6770              :       HNDLE hDB;
    6771            2 :       const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
    6772              : 
    6773            2 :       bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
    6774              : 
    6775            2 :       if (!buffer_name || !buffer_name[0]) {
    6776            0 :          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
    6777            2 :          return BM_INVALID_PARAM;
    6778              :       }
    6779              : 
    6780            2 :       if (strlen(buffer_name) >= NAME_LENGTH) {
    6781            0 :          cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
    6782            0 :          return BM_INVALID_PARAM;
    6783              :       }
    6784              : 
    6785            2 :       status = cm_get_experiment_database(&hDB, NULL);
    6786              : 
    6787            2 :       if (status != SUCCESS || hDB == 0) {
    6788              :          //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
    6789            0 :          return BM_NO_SHM;
    6790              :       }
    6791              : 
    6792              :       /* get buffer size from ODB, user parameter as default if not present in ODB */
    6793            2 :       std::string odb_path;
    6794            2 :       odb_path += "/Experiment/Buffer sizes/";
    6795            2 :       odb_path += buffer_name;
    6796              : 
    6797            2 :       int size = sizeof(INT);
    6798            2 :       status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
    6799              : 
    6800            2 :       if (buffer_size <= 0 || buffer_size > max_buffer_size) {
    6801            0 :          cm_msg(MERROR, "bm_open_buffer",
    6802              :                 "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
    6803              :                 buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
    6804            0 :          return BM_INVALID_PARAM;
    6805              :       }
    6806              : 
    6807            2 :       _bm_max_event_size = DEFAULT_MAX_EVENT_SIZE;
    6808              : 
    6809            2 :       size = sizeof(INT);
    6810            2 :       status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
    6811              : 
    6812            2 :       if (status != DB_SUCCESS) {
    6813            0 :          cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
    6814              :                 status);
    6815            0 :          return status;
    6816              :       }
    6817              : 
    6818              :       /* check if buffer already is open */
    6819            2 :       gBuffersMutex.lock();
    6820            2 :       for (size_t i = 0; i < gBuffers.size(); i++) {
    6821            0 :          BUFFER* pbuf = gBuffers[i];
    6822            0 :          if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
    6823            0 :             *buffer_handle = i + 1;
    6824            0 :             gBuffersMutex.unlock();
    6825            0 :             return BM_SUCCESS;
    6826              :          }
    6827              :       }
    6828            2 :       gBuffersMutex.unlock();
    6829              : 
    6830              :       // only one thread at a time should create new buffers
    6831              : 
    6832              :       static std::mutex gNewBufferMutex;
    6833            2 :       std::lock_guard<std::mutex> guard(gNewBufferMutex);
    6834              : 
    6835              :       // if we had a race against another thread
    6836              :       // and while we were waiting for gNewBufferMutex
    6837              :       // the other thread created this buffer, we return it.
    6838              : 
    6839            2 :       gBuffersMutex.lock();
    6840            2 :       for (size_t i = 0; i < gBuffers.size(); i++) {
    6841            0 :          BUFFER* pbuf = gBuffers[i];
    6842            0 :          if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
    6843            0 :             *buffer_handle = i + 1;
    6844            0 :             gBuffersMutex.unlock();
    6845            0 :             return BM_SUCCESS;
    6846              :          }
    6847              :       }
    6848            2 :       gBuffersMutex.unlock();
    6849              : 
    6850              :       /* allocate new BUFFER object */
    6851              : 
    6852            2 :       BUFFER* pbuf = new BUFFER;
    6853              : 
    6854              :       /* there is no constructor for BUFFER object, we have to zero the arrays manually */
    6855              : 
    6856          130 :       for (int i=0; i<MAX_CLIENTS; i++) {
    6857          128 :          pbuf->client_count_write_wait[i] = 0;
    6858          128 :          pbuf->client_time_write_wait[i] = 0;
    6859              :       }
    6860              : 
    6861              :       /* create buffer semaphore */
    6862              : 
    6863            2 :       status = ss_semaphore_create(buffer_name, &(pbuf->semaphore));
    6864              : 
    6865            2 :       if (status != SS_CREATED && status != SS_SUCCESS) {
    6866            0 :          *buffer_handle = 0;
    6867            0 :          delete pbuf;
    6868            0 :          return BM_NO_SEMAPHORE;
    6869              :       }
    6870              : 
    6871            2 :       std::string client_name = cm_get_client_name();
    6872              : 
    6873              :       /* store client name */
    6874            2 :       mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
    6875              : 
    6876              :       /* store buffer name */
    6877            2 :       mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
    6878              : 
    6879              :       /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
    6880              : 
    6881            2 :       pbuf->attached = true; // required by bm_lock_buffer()
    6882              : 
    6883            2 :       bm_lock_buffer_guard pbuf_guard(pbuf);
    6884              : 
    6885            2 :       if (!pbuf_guard.is_locked()) {
    6886              :          // cannot happen, no other thread can see this pbuf
    6887            0 :          abort();
    6888              :          return BM_NO_SEMAPHORE;
    6889              :       }
    6890              : 
    6891              :       /* open shared memory */
    6892              : 
    6893            2 :       void *p = NULL;
    6894            2 :       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
    6895              : 
    6896            2 :       if (status != SS_SUCCESS && status != SS_CREATED) {
    6897            0 :          *buffer_handle = 0;
    6898            0 :          pbuf_guard.unlock();
    6899            0 :          pbuf_guard.invalidate(); // destructor will see a deleted pbuf
    6900            0 :          delete pbuf;
    6901            0 :          return BM_NO_SHM;
    6902              :       }
    6903              : 
    6904            2 :       pbuf->buffer_header = (BUFFER_HEADER *) p;
    6905              : 
    6906            2 :       BUFFER_HEADER *pheader = pbuf->buffer_header;
    6907              : 
    6908            2 :       bool shm_created = (status == SS_CREATED);
    6909              : 
    6910            2 :       if (shm_created) {
    6911              :          /* initialize newly created shared memory */
    6912              : 
    6913            2 :          memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
    6914              : 
    6915            2 :          mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
    6916            2 :          pheader->size = buffer_size;
    6917              : 
    6918              :       } else {
    6919              :          /* validate existing shared memory */
    6920              : 
    6921            0 :          if (!equal_ustring(pheader->name, buffer_name)) {
    6922              :             // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
    6923            0 :             pbuf_guard.unlock();
    6924            0 :             pbuf_guard.invalidate(); // destructor will see a deleted pbuf
    6925            0 :             cm_msg(MERROR, "bm_open_buffer",
    6926              :                    "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
    6927            0 :                    pheader->name);
    6928            0 :             *buffer_handle = 0;
    6929            0 :             delete pbuf;
    6930            0 :             return BM_CORRUPTED;
    6931              :          }
    6932              : 
    6933            0 :          if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
    6934              :             // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
    6935            0 :             pbuf_guard.unlock();
    6936            0 :             pbuf_guard.invalidate(); // destructor will see a deleted pbuf
    6937            0 :             cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
    6938              :                    buffer_name, pheader->num_clients, MAX_CLIENTS);
    6939            0 :             *buffer_handle = 0;
    6940            0 :             delete pbuf;
    6941            0 :             return BM_CORRUPTED;
    6942              :          }
    6943              : 
    6944            0 :          if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
    6945              :             // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
    6946            0 :             pbuf_guard.unlock();
    6947            0 :             pbuf_guard.invalidate(); // destructor will see a deleted pbuf
    6948            0 :             cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
    6949              :                    buffer_name, pheader->max_client_index, MAX_CLIENTS);
    6950            0 :             *buffer_handle = 0;
    6951            0 :             delete pbuf;
    6952            0 :             return BM_CORRUPTED;
    6953              :          }
    6954              : 
    6955              :          /* check if buffer size is identical */
    6956            0 :          if (pheader->size != buffer_size) {
    6957            0 :             cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
    6958              :                    buffer_name, buffer_size, pheader->size);
    6959              : 
    6960            0 :             buffer_size = pheader->size;
    6961              : 
    6962            0 :             ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
    6963              : 
    6964            0 :             status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
    6965              : 
    6966            0 :             if (status != SS_SUCCESS) {
    6967            0 :                *buffer_handle = 0;
    6968            0 :                pbuf_guard.unlock();
    6969            0 :                pbuf_guard.invalidate(); // destructor will see a deleted pbuf
    6970            0 :                delete pbuf;
    6971            0 :                return BM_NO_SHM;
    6972              :             }
    6973              : 
    6974            0 :             pbuf->buffer_header = (BUFFER_HEADER *) p;
    6975            0 :             pheader = pbuf->buffer_header;
    6976              :          }
    6977              :       }
    6978              : 
    6979              :       /* shared memory is good from here down */
    6980              : 
    6981            2 :       pbuf->attached = true;
    6982              : 
    6983            2 :       pbuf->shm_handle = shm_handle;
    6984            2 :       pbuf->shm_size = shm_size;
    6985            2 :       pbuf->callback = FALSE;
    6986              : 
    6987            2 :       bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
    6988              : 
    6989            2 :       status = bm_validate_buffer_locked(pbuf);
    6990            2 :       if (status != BM_SUCCESS) {
    6991            0 :          cm_msg(MERROR, "bm_open_buffer",
    6992              :                 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
    6993              :                 status);
    6994            0 :          bm_reset_buffer_locked(pbuf);
    6995            0 :          cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
    6996              :       }
    6997              : 
    6998              :       /* add our client BUFFER_HEADER */
    6999              : 
    7000            2 :       int iclient = 0;
    7001            2 :       for (; iclient < MAX_CLIENTS; iclient++)
    7002            2 :          if (pheader->client[iclient].pid == 0)
    7003            2 :             break;
    7004              : 
    7005            2 :       if (iclient == MAX_CLIENTS) {
    7006            0 :          *buffer_handle = 0;
    7007              :          // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
    7008            0 :          pbuf_guard.unlock();
    7009            0 :          pbuf_guard.invalidate(); // destructor will see a deleted pbuf
    7010            0 :          delete pbuf;
    7011            0 :          cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
    7012            0 :          return BM_NO_SLOT;
    7013              :       }
    7014              : 
    7015              :       /* store slot index in _buffer structure */
    7016            2 :       pbuf->client_index = iclient;
    7017              : 
    7018              :       /*
    7019              :          Save the index of the last client of that buffer so that later only
    7020              :          the clients 0..max_client_index-1 have to be searched through.
    7021              :        */
    7022            2 :       pheader->num_clients++;
    7023            2 :       if (iclient + 1 > pheader->max_client_index)
    7024            2 :          pheader->max_client_index = iclient + 1;
    7025              : 
    7026              :       /* setup buffer header and client structure */
    7027            2 :       BUFFER_CLIENT *pclient = &pheader->client[iclient];
    7028              : 
    7029            2 :       memset(pclient, 0, sizeof(BUFFER_CLIENT));
    7030              : 
    7031            2 :       mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
    7032              : 
    7033            2 :       pclient->pid = ss_getpid();
    7034              : 
    7035            2 :       ss_suspend_get_buffer_port(ss_gettid(), &pclient->port);
    7036              : 
    7037            2 :       pclient->read_pointer = pheader->write_pointer;
    7038            2 :       pclient->last_activity = ss_millitime();
    7039              : 
    7040            2 :       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
    7041              : 
    7042            2 :       pbuf_guard.unlock();
    7043              : 
    7044              :       /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
    7045              : 
    7046            2 :       pheader = NULL;
    7047              : 
    7048              :       /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
    7049              : 
    7050            2 :       bm_clear_buffer_statistics(hDB, pbuf);
    7051            2 :       bm_write_buffer_statistics_to_odb(hDB, pbuf, true);
    7052              : 
    7053              :       /* add pbuf to buffer list */
    7054              : 
    7055            2 :       gBuffersMutex.lock();
    7056              : 
    7057            2 :       bool added = false;
    7058            2 :       for (size_t i=0; i<gBuffers.size(); i++) {
    7059            0 :          if (gBuffers[i] == NULL) {
    7060            0 :             gBuffers[i] = pbuf;
    7061            0 :             added = true;
    7062            0 :             *buffer_handle = i+1;
    7063            0 :             break;
    7064              :          }
    7065              :       }
    7066            2 :       if (!added) {
    7067            2 :          *buffer_handle = gBuffers.size() + 1;
    7068            2 :          gBuffers.push_back(pbuf);
    7069              :       }
    7070              : 
    7071              :       /* from here down we should not touch pbuf without locking it */
    7072              : 
    7073            2 :       pbuf = NULL;
    7074              : 
    7075            2 :       gBuffersMutex.unlock();
    7076              : 
    7077              :       /* new buffer is now ready for use */
    7078              : 
    7079              :       /* initialize buffer counters */
    7080            2 :       bm_init_buffer_counters(*buffer_handle);
    7081              : 
    7082            2 :       bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
    7083              : 
    7084            2 :       if (shm_created)
    7085            2 :          return BM_CREATED;
    7086            8 :    }
    7087              : #endif                          /* LOCAL_ROUTINES */
    7088              : 
    7089            0 :    return BM_SUCCESS;
    7090              : }
    7091              : 
    7092              : /********************************************************************/
    7093              : /**
    7094              : If buffer is already open, return it's handle
    7095              : @param buffer_name buffer name
    7096              : @return BM_SUCCESS, BM_NOT_FOUND
    7097              : */
    7098            0 : INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
    7099              : {
    7100            0 :    gBuffersMutex.lock();
    7101            0 :    for (size_t i = 0; i < gBuffers.size(); i++) {
    7102            0 :       BUFFER* pbuf = gBuffers[i];
    7103            0 :       if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
    7104            0 :          *buffer_handle = i + 1;
    7105            0 :          gBuffersMutex.unlock();
    7106            0 :          return BM_SUCCESS;
    7107              :       }
    7108              :    }
    7109            0 :    gBuffersMutex.unlock();
    7110            0 :    return BM_NOT_FOUND;
    7111              : }
    7112              : 
    7113              : /********************************************************************/
    7114              : /**
    7115              : Closes an event buffer previously opened with bm_open_buffer().
    7116              : @param buffer_handle buffer handle
    7117              : @return BM_SUCCESS, BM_INVALID_HANDLE
    7118              : */
    7119            4 : INT bm_close_buffer(INT buffer_handle) {
    7120              :    //printf("bm_close_buffer: handle %d\n", buffer_handle);
    7121              : 
    7122            4 :    if (rpc_is_remote())
    7123            0 :       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
    7124              : 
    7125              : #ifdef LOCAL_ROUTINES
    7126              :    {
    7127            4 :       int status = 0;
    7128              : 
    7129            4 :       BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
    7130              : 
    7131            4 :       if (!pbuf)
    7132            2 :          return status;
    7133              : 
    7134              :       //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
    7135              : 
    7136              :       int i;
    7137              : 
    7138              :       { /* delete all requests for this buffer */
    7139            2 :          _request_list_mutex.lock();
    7140            2 :          std::vector<EventRequest> request_list_copy = _request_list;
    7141            2 :          _request_list_mutex.unlock();
    7142            2 :          for (size_t i = 0; i < request_list_copy.size(); i++) {
    7143            0 :             if (request_list_copy[i].buffer_handle == buffer_handle) {
    7144            0 :                bm_delete_request(i);
    7145              :             }
    7146              :          }
    7147            2 :       }
    7148              : 
    7149              :       HNDLE hDB;
    7150            2 :       cm_get_experiment_database(&hDB, NULL);
    7151              : 
    7152            2 :       if (hDB) {
    7153              :          /* write statistics to odb */
    7154            2 :          bm_write_buffer_statistics_to_odb(hDB, pbuf, TRUE);
    7155              :       }
    7156              : 
    7157              :       /* lock buffer in correct order */
    7158              : 
    7159            2 :       status = bm_lock_buffer_read_cache(pbuf);
    7160              : 
    7161            2 :       if (status != BM_SUCCESS) {
    7162            0 :          return status;
    7163              :       }
    7164              : 
    7165            2 :       status = bm_lock_buffer_write_cache(pbuf);
    7166              : 
    7167            2 :       if (status != BM_SUCCESS) {
    7168            0 :          pbuf->read_cache_mutex.unlock();
    7169            0 :          return status;
    7170              :       }
    7171              : 
    7172            2 :       bm_lock_buffer_guard pbuf_guard(pbuf);
    7173              : 
    7174            2 :       if (!pbuf_guard.is_locked()) {
    7175            0 :          pbuf->write_cache_mutex.unlock();
    7176            0 :          pbuf->read_cache_mutex.unlock();
    7177            0 :          return pbuf_guard.get_status();
    7178              :       }
    7179              : 
    7180            2 :       BUFFER_HEADER *pheader = pbuf->buffer_header;
    7181              : 
    7182              :       /* mark entry in _buffer as empty */
    7183            2 :       pbuf->attached = false;
    7184              : 
    7185            2 :       BUFFER_CLIENT* pclient = bm_get_my_client_locked(pbuf_guard);
    7186              : 
    7187            2 :       if (pclient) {
    7188              :          /* clear entry from client structure in buffer header */
    7189            2 :          memset(pclient, 0, sizeof(BUFFER_CLIENT));
    7190              :       }
    7191              : 
    7192              :       /* calculate new max_client_index entry */
    7193          130 :       for (i = MAX_CLIENTS - 1; i >= 0; i--)
    7194          128 :          if (pheader->client[i].pid != 0)
    7195            0 :             break;
    7196            2 :       pheader->max_client_index = i + 1;
    7197              : 
    7198              :       /* count new number of clients */
    7199            2 :       int j = 0;
    7200          130 :       for (i = MAX_CLIENTS - 1; i >= 0; i--)
    7201          128 :          if (pheader->client[i].pid != 0)
    7202            0 :             j++;
    7203            2 :       pheader->num_clients = j;
    7204              : 
    7205            2 :       int destroy_flag = (pheader->num_clients == 0);
    7206              : 
    7207              :       // we hold the locks on the read cache and the write cache.
    7208              : 
    7209              :       /* free cache */
    7210            2 :       if (pbuf->read_cache_size > 0) {
    7211            0 :          free(pbuf->read_cache);
    7212            0 :          pbuf->read_cache = NULL;
    7213            0 :          pbuf->read_cache_size = 0;
    7214            0 :          pbuf->read_cache_rp = 0;
    7215            0 :          pbuf->read_cache_wp = 0;
    7216              :       }
    7217              : 
    7218            2 :       if (pbuf->write_cache_size > 0) {
    7219            0 :          free(pbuf->write_cache);
    7220            0 :          pbuf->write_cache = NULL;
    7221            0 :          pbuf->write_cache_size = 0;
    7222            0 :          pbuf->write_cache_rp = 0;
    7223            0 :          pbuf->write_cache_wp = 0;
    7224              :       }
    7225              : 
    7226              :       /* check if anyone is waiting and wake him up */
    7227              : 
    7228            2 :       for (int i = 0; i < pheader->max_client_index; i++) {
    7229            0 :          BUFFER_CLIENT *pclient = pheader->client + i;
    7230            0 :          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
    7231            0 :             ss_resume(pclient->port, "B  ");
    7232              :       }
    7233              : 
    7234              :       /* unmap shared memory, delete it if we are the last */
    7235              : 
    7236            2 :       ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
    7237              : 
    7238              :       /* after ss_shm_close() these are invalid: */
    7239              : 
    7240            2 :       pheader = NULL;
    7241            2 :       pbuf->buffer_header = NULL;
    7242            2 :       pbuf->shm_size = 0;
    7243            2 :       pbuf->shm_handle = 0;
    7244              : 
    7245              :       /* unlock buffer in correct order */
    7246              : 
    7247            2 :       pbuf_guard.unlock();
    7248              : 
    7249            2 :       pbuf->write_cache_mutex.unlock();
    7250            2 :       pbuf->read_cache_mutex.unlock();
    7251              : 
    7252              :       /* delete semaphore */
    7253              : 
    7254            2 :       ss_semaphore_delete(pbuf->semaphore, destroy_flag);
    7255            2 :    }
    7256              : #endif                          /* LOCAL_ROUTINES */
    7257              : 
    7258            2 :    return BM_SUCCESS;
    7259              : }
    7260              : 
    7261              : /********************************************************************/
    7262              : /**
    7263              : Close all open buffers
    7264              : @return BM_SUCCESS
    7265              : */
    7266            2 : INT bm_close_all_buffers(void) {
    7267            2 :    if (rpc_is_remote())
    7268            0 :       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
    7269              : 
    7270              : #ifdef LOCAL_ROUTINES
    7271              :    {
    7272            2 :       cm_msg_close_buffer();
    7273              : 
    7274            2 :       gBuffersMutex.lock();
    7275            2 :       size_t nbuf = gBuffers.size();
    7276            2 :       gBuffersMutex.unlock();
    7277              : 
    7278            4 :       for (size_t i = nbuf; i > 0; i--) {
    7279            2 :          bm_close_buffer(i);
    7280              :       }
    7281              : 
    7282            2 :       gBuffersMutex.lock();
    7283            4 :       for (size_t i=0; i< gBuffers.size(); i++) {
    7284            2 :          BUFFER* pbuf = gBuffers[i];
    7285            2 :          if (!pbuf)
    7286            0 :             continue;
    7287            2 :          delete pbuf;
    7288            2 :          pbuf = NULL;
    7289            2 :          gBuffers[i] = NULL;
    7290              :       }
    7291            2 :       gBuffersMutex.unlock();
    7292              :    }
    7293              : #endif                          /* LOCAL_ROUTINES */
    7294              : 
    7295            2 :    return BM_SUCCESS;
    7296              : }
    7297              : 
    7298              : /********************************************************************/
    7299              : /**
    7300              : Close all open buffers
    7301              : @return BM_SUCCESS
    7302              : */
    7303            0 : INT bm_write_statistics_to_odb(void) {
    7304              : #ifdef LOCAL_ROUTINES
    7305              :    {
    7306              :       int status;
    7307              :       HNDLE hDB;
    7308              : 
    7309            0 :       status = cm_get_experiment_database(&hDB, NULL);
    7310              : 
    7311            0 :       if (status != CM_SUCCESS) {
    7312              :          //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
    7313            0 :          return BM_SUCCESS;
    7314              :       }
    7315              : 
    7316            0 :       std::vector<BUFFER*> mybuffers;
    7317              :       
    7318            0 :       gBuffersMutex.lock();
    7319            0 :       mybuffers = gBuffers;
    7320            0 :       gBuffersMutex.unlock();
    7321              : 
    7322            0 :       for (BUFFER* pbuf : mybuffers) {
    7323            0 :          if (!pbuf || !pbuf->attached)
    7324            0 :             continue;
    7325            0 :          bm_write_buffer_statistics_to_odb(hDB, pbuf, FALSE);
    7326              :       }
    7327            0 :    }
    7328              : #endif /* LOCAL_ROUTINES */
    7329              : 
    7330            0 :    return BM_SUCCESS;
    7331              : }
    7332              : 
    7333              : /**dox***************************************************************/
    7334              : /** @} *//* end of bmfunctionc */
    7335              : 
    7336              : /**dox***************************************************************/
    7337              : /** @addtogroup cmfunctionc
    7338              :  *
    7339              :  *  @{  */
    7340              : 
    7341              : /*-- Watchdog routines ---------------------------------------------*/
    7342              : #ifdef LOCAL_ROUTINES
    7343              : 
    7344              : static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
    7345              : static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
    7346              : static std::atomic<std::thread*> _watchdog_thread{NULL};
    7347              : 
    7348              : /********************************************************************/
    7349              : /**
    7350              : Watchdog thread to maintain the watchdog timeout timestamps for this client
    7351              : */
    7352            0 : INT cm_watchdog_thread(void *unused) {
    7353            0 :    _watchdog_thread_is_running = true;
    7354              :    //printf("cm_watchdog_thread started!\n");
    7355            0 :    while (_watchdog_thread_run) {
    7356              :       //printf("cm_watchdog_thread runs!\n");
    7357            0 :       DWORD now = ss_millitime();
    7358            0 :       bm_update_last_activity(now);
    7359            0 :       db_update_last_activity(now);
    7360              :       int i;
    7361            0 :       for (i = 0; i < 20; i++) {
    7362            0 :          ss_sleep(100);
    7363            0 :          if (!_watchdog_thread_run)
    7364            0 :             break;
    7365              :       }
    7366              :    }
    7367              :    //printf("cm_watchdog_thread stopped!\n");
    7368            0 :    _watchdog_thread_is_running = false;
    7369            0 :    return 0;
    7370              : }
    7371              : 
    7372            0 : static void xcm_watchdog_thread() {
    7373            0 :    cm_watchdog_thread(NULL);
    7374            0 : }
    7375              : 
    7376              : #endif
    7377              : 
    7378            0 : INT cm_start_watchdog_thread() {
    7379              :    /* watchdog does not run inside remote clients.
    7380              :     * watchdog timeout timers are maintained by the mserver */
    7381            0 :    if (rpc_is_remote())
    7382            0 :       return CM_SUCCESS;
    7383              : #ifdef LOCAL_ROUTINES
    7384              :    /* only start once */
    7385            0 :    if (_watchdog_thread)
    7386            0 :       return CM_SUCCESS;
    7387            0 :    _watchdog_thread_run = true;
    7388            0 :    _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
    7389              : #endif
    7390            0 :    return CM_SUCCESS;
    7391              : }
    7392              : 
    7393            2 : INT cm_stop_watchdog_thread() {
    7394              :    /* watchdog does not run inside remote clients.
    7395              :     * watchdog timeout timers are maintained by the mserver */
    7396            2 :    if (rpc_is_remote())
    7397            0 :       return CM_SUCCESS;
    7398              : #ifdef LOCAL_ROUTINES
    7399            2 :    _watchdog_thread_run = false;
    7400            2 :    while (_watchdog_thread_is_running) {
    7401              :       //printf("waiting for watchdog thread to shut down\n");
    7402            0 :       ss_sleep(10);
    7403              :    }
    7404            2 :    if (_watchdog_thread != NULL) {
    7405            0 :       _watchdog_thread.load()->join();
    7406            0 :       delete static_cast<std::thread *>(_watchdog_thread);
    7407            0 :       _watchdog_thread = NULL;
    7408              :    }
    7409              : #endif
    7410            2 :    return CM_SUCCESS;
    7411              : }
    7412              : 
    7413              : /********************************************************************/
    7414              : /**
    7415              : Shutdown (exit) other MIDAS client
    7416              : @param name           Client name or "all" for all clients
    7417              : @param bUnique        If true, look for the exact client name.
    7418              :                       If false, look for namexxx where xxx is
    7419              :                       a any number.
    7420              : 
    7421              : @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY
    7422              : */
    7423            0 : INT cm_shutdown(const char *name, BOOL bUnique) {
    7424              :    INT status, return_status, i, size;
    7425              :    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
    7426              :    KEY key;
    7427              :    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
    7428              :    INT port;
    7429              :    DWORD start_time;
    7430              :    DWORD timeout;
    7431              :    DWORD last;
    7432              : 
    7433            0 :    cm_get_experiment_database(&hDB, &hKeyClient);
    7434              : 
    7435            0 :    status = db_find_key(hDB, 0, "System/Clients", &hKey);
    7436            0 :    if (status != DB_SUCCESS)
    7437            0 :       return DB_NO_KEY;
    7438              : 
    7439            0 :    return_status = CM_NO_CLIENT;
    7440              : 
    7441              :    /* loop over all clients */
    7442            0 :    for (i = 0;; i++) {
    7443            0 :       status = db_enum_key(hDB, hKey, i, &hSubkey);
    7444            0 :       if (status == DB_NO_MORE_SUBKEYS)
    7445            0 :          break;
    7446              : 
    7447              :       /* don't shutdown ourselves */
    7448            0 :       if (hSubkey == hKeyClient)
    7449            0 :          continue;
    7450              : 
    7451            0 :       if (status == DB_SUCCESS) {
    7452            0 :          db_get_key(hDB, hSubkey, &key);
    7453              : 
    7454              :          /* contact client */
    7455            0 :          size = sizeof(client_name);
    7456            0 :          status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
    7457            0 :          if (status != DB_SUCCESS)
    7458            0 :             continue;
    7459              : 
    7460            0 :          if (!bUnique)
    7461            0 :             client_name[strlen(name)] = 0;      /* strip number */
    7462              : 
    7463              :          /* check if individual client */
    7464            0 :          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
    7465            0 :             continue;
    7466              : 
    7467            0 :          size = sizeof(port);
    7468            0 :          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
    7469              : 
    7470            0 :          size = sizeof(remote_host);
    7471            0 :          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
    7472              : 
    7473            0 :          cm_get_watchdog_info(hDB, name, &timeout, &last);
    7474            0 :          if (timeout == 0)
    7475            0 :             timeout = 5000;
    7476              : 
    7477              :          /* client found -> connect to its server port */
    7478            0 :          status = rpc_client_connect(remote_host, port, client_name, &hConn);
    7479            0 :          if (status != RPC_SUCCESS) {
    7480            0 :             int client_pid = atoi(key.name);
    7481            0 :             return_status = CM_NO_CLIENT;
    7482            0 :             cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
    7483              :                    client_name, remote_host, port);
    7484              : #ifdef SIGKILL
    7485            0 :             cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
    7486              :                    client_pid);
    7487            0 :             kill(client_pid, SIGKILL);
    7488            0 :             return_status = CM_SUCCESS;
    7489            0 :             status = cm_delete_client_info(hDB, client_pid);
    7490            0 :             if (status != CM_SUCCESS)
    7491            0 :                cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
    7492              :                       name, client_pid, status);
    7493              : #endif
    7494              :          } else {
    7495              :             /* call disconnect with shutdown=TRUE */
    7496            0 :             rpc_client_disconnect(hConn, TRUE);
    7497              : 
    7498              :             /* wait until client has shut down */
    7499            0 :             start_time = ss_millitime();
    7500              :             do {
    7501            0 :                ss_sleep(100);
    7502            0 :                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
    7503            0 :             } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
    7504              : 
    7505            0 :             if (status == DB_SUCCESS) {
    7506            0 :                int client_pid = atoi(key.name);
    7507            0 :                return_status = CM_NO_CLIENT;
    7508            0 :                cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
    7509              : #ifdef SIGKILL
    7510            0 :                cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
    7511              :                       client_pid);
    7512            0 :                kill(client_pid, SIGKILL);
    7513            0 :                status = cm_delete_client_info(hDB, client_pid);
    7514            0 :                if (status != CM_SUCCESS)
    7515            0 :                   cm_msg(MERROR, "cm_shutdown",
    7516              :                          "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
    7517              :                          status);
    7518              : #endif
    7519            0 :                return_status = CM_NO_CLIENT;
    7520              :             } else {
    7521            0 :                return_status = CM_SUCCESS;
    7522            0 :                i--;
    7523              :             }
    7524              :          }
    7525              :       }
    7526              : 
    7527              :       /* display any message created during each shutdown */
    7528            0 :       cm_msg_flush_buffer();
    7529            0 :    }
    7530              : 
    7531            0 :    return return_status;
    7532              : }
    7533              : 
    7534              : /********************************************************************/
    7535              : /**
    7536              : Check if a MIDAS client exists in current experiment
    7537              : @param    name            Client name
    7538              : @param    bUnique         If true, look for the exact client name.
    7539              :                           If false, look for namexxx where xxx is
    7540              :                           a any number
    7541              : @return   CM_SUCCESS, CM_NO_CLIENT
    7542              : */
    7543            0 : INT cm_exist(const char *name, BOOL bUnique) {
    7544              :    INT status, i, size;
    7545              :    HNDLE hDB, hKeyClient, hKey, hSubkey;
    7546              :    char client_name[NAME_LENGTH];
    7547              : 
    7548            0 :    if (rpc_is_remote())
    7549            0 :       return rpc_call(RPC_CM_EXIST, name, bUnique);
    7550              : 
    7551            0 :    cm_get_experiment_database(&hDB, &hKeyClient);
    7552              : 
    7553            0 :    status = db_find_key(hDB, 0, "System/Clients", &hKey);
    7554            0 :    if (status != DB_SUCCESS)
    7555            0 :       return DB_NO_KEY;
    7556              : 
    7557            0 :    db_lock_database(hDB);
    7558              : 
    7559              :    /* loop over all clients */
    7560            0 :    for (i = 0;; i++) {
    7561            0 :       status = db_enum_key(hDB, hKey, i, &hSubkey);
    7562            0 :       if (status == DB_NO_MORE_SUBKEYS)
    7563            0 :          break;
    7564              : 
    7565            0 :       if (hSubkey == hKeyClient)
    7566            0 :          continue;
    7567              : 
    7568            0 :       if (status == DB_SUCCESS) {
    7569              :          /* get client name */
    7570            0 :          size = sizeof(client_name);
    7571            0 :          status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
    7572              : 
    7573            0 :          if (status != DB_SUCCESS) {
    7574              :             //fprintf(stderr, "cm_exist: name %s, i=%d, hSubkey=%d, status %d, client_name %s, my name %s\n", name, i, hSubkey, status, client_name, _client_name);
    7575            0 :             continue;
    7576              :          }
    7577              : 
    7578            0 :          if (equal_ustring(client_name, name)) {
    7579            0 :             db_unlock_database(hDB);
    7580            0 :             return CM_SUCCESS;
    7581              :          }
    7582              : 
    7583            0 :          if (!bUnique) {
    7584            0 :             client_name[strlen(name)] = 0;      /* strip number */
    7585            0 :             if (equal_ustring(client_name, name)) {
    7586            0 :                db_unlock_database(hDB);
    7587            0 :                return CM_SUCCESS;
    7588              :             }
    7589              :          }
    7590              :       }
    7591              :    }
    7592              : 
    7593            0 :    db_unlock_database(hDB);
    7594              : 
    7595            0 :    return CM_NO_CLIENT;
    7596              : }
    7597              : 
    7598              : /********************************************************************/
    7599              : /**
    7600              : Remove hanging clients independent of their watchdog
    7601              :            timeout.
    7602              : 
    7603              : Since this function does not obey the client watchdog
    7604              : timeout, it should be only called to remove clients which
    7605              : have their watchdog checking turned off or which are
    7606              : known to be dead. The normal client removement is done
    7607              : via cm_watchdog().
    7608              : 
    7609              : Currently (Sept. 02) there are two applications for that:
    7610              : -# The ODBEdit command "cleanup", which can be used to
    7611              : remove clients which have their watchdog checking off,
    7612              : like the analyzer started with the "-d" flag for a
    7613              : debugging session.
    7614              : -# The frontend init code to remove previous frontends.
    7615              : This can be helpful if a frontend dies. Normally,
    7616              : one would have to wait 60 sec. for a crashed frontend
    7617              : to be removed. Only then one can start again the
    7618              : frontend. Since the frontend init code contains a
    7619              : call to cm_cleanup(<frontend_name>), one can restart
    7620              : a frontend immediately.
    7621              : 
    7622              : Added ignore_timeout on Nov.03. A logger might have an
    7623              : increased tiemout of up to 60 sec. because of tape
    7624              : operations. If ignore_timeout is FALSE, the logger is
    7625              : then not killed if its inactivity is less than 60 sec.,
    7626              : while in the previous implementation it was always
    7627              : killed after 2*WATCHDOG_INTERVAL.
    7628              : @param    client_name      Client name, if zero check all clients
    7629              : @param    ignore_timeout   If TRUE, ignore a possible increased
    7630              :                            timeout defined by each client.
    7631              : @return   CM_SUCCESS
    7632              : */
    7633            0 : INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
    7634            0 :    if (rpc_is_remote())
    7635            0 :       return rpc_call(RPC_CM_CLEANUP, client_name);
    7636              : 
    7637              : #ifdef LOCAL_ROUTINES
    7638              :    {
    7639              :       DWORD interval;
    7640            0 :       DWORD now = ss_millitime();
    7641              : 
    7642            0 :       std::vector<BUFFER*> mybuffers;
    7643              :       
    7644            0 :       gBuffersMutex.lock();
    7645            0 :       mybuffers = gBuffers;
    7646            0 :       gBuffersMutex.unlock();
    7647              : 
    7648              :       /* check buffers */
    7649            0 :       for (BUFFER* pbuf : mybuffers) {
    7650            0 :          if (!pbuf)
    7651            0 :             continue;
    7652            0 :          if (pbuf->attached) {
    7653            0 :             std::string msg;
    7654              : 
    7655            0 :             bm_lock_buffer_guard pbuf_guard(pbuf);
    7656              : 
    7657            0 :             if (!pbuf_guard.is_locked())
    7658            0 :                continue;
    7659              : 
    7660              :             /* update the last_activity entry to show that we are alive */
    7661            0 :             BUFFER_HEADER *pheader = pbuf->buffer_header;
    7662            0 :             BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
    7663            0 :             pclient->last_activity = ss_millitime();
    7664              : 
    7665              :             /* now check other clients */
    7666            0 :             for (int j = 0; j < pheader->max_client_index; j++) {
    7667            0 :                BUFFER_CLIENT *pbclient = &pheader->client[j];
    7668            0 :                if (j != pbuf->client_index && pbclient->pid &&
    7669            0 :                    (client_name == NULL || client_name[0] == 0
    7670            0 :                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
    7671            0 :                   if (ignore_timeout)
    7672            0 :                      interval = 2 * WATCHDOG_INTERVAL;
    7673              :                   else
    7674            0 :                      interval = pbclient->watchdog_timeout;
    7675              : 
    7676              :                   /* If client process has no activity, clear its buffer entry. */
    7677            0 :                   if (interval > 0
    7678            0 :                       && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
    7679              : 
    7680              :                      /* now make again the check with the buffer locked */
    7681            0 :                      if (interval > 0
    7682            0 :                          && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
    7683            0 :                         msg = msprintf(
    7684              :                                 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
    7685            0 :                                 pbclient->name, pheader->name,
    7686            0 :                                 (ss_millitime() - pbclient->last_activity) / 1000.0,
    7687            0 :                                 interval / 1000.0);
    7688              : 
    7689            0 :                         bm_remove_client_locked(pheader, j);
    7690              :                      }
    7691              : 
    7692              :                      /* go again through whole list */
    7693            0 :                      j = 0;
    7694              :                   }
    7695              :                }
    7696              :             }
    7697              : 
    7698              :             // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
    7699            0 :             pbuf_guard.unlock();
    7700              : 
    7701              :             /* display info message after unlocking buffer */
    7702            0 :             if (!msg.empty())
    7703            0 :                cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
    7704            0 :          }
    7705              :       }
    7706              : 
    7707            0 :       db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
    7708            0 :    }
    7709              : #endif                          /* LOCAL_ROUTINES */
    7710              : 
    7711            0 :    return CM_SUCCESS;
    7712              : }
    7713              : 
    7714              : /********************************************************************/
    7715              : /**
    7716              : Expand environment variables in filesystem file path names
    7717              : 
    7718              : Examples of expansion: $FOO=foo, $BAR=bar, $UNDEF is undefined (undefined, not empty)
    7719              : 
    7720              :    ok &= test_cm_expand_env1("aaa", "aaa");
    7721              :    ok &= test_cm_expand_env1("$FOO", "foo");
    7722              :    ok &= test_cm_expand_env1("/$FOO", "/foo");
    7723              :    ok &= test_cm_expand_env1("/$FOO/", "/foo/");
    7724              :    ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
    7725              :    ok &= test_cm_expand_env1("$FOO1", "$FOO1");
    7726              :    ok &= test_cm_expand_env1("1$FOO", "1foo");
    7727              :    ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
    7728              :    ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
    7729              : 
    7730              : @param    str     Input file path
    7731              : @return   expanded file path
    7732              : */
    7733            0 : std::string cm_expand_env(const char *str) {
    7734            0 :    const char *s = str;
    7735            0 :    std::string r;
    7736            0 :    for (; *s;) {
    7737            0 :       if (*s == '$') {
    7738            0 :          s++;
    7739            0 :          std::string envname;
    7740            0 :          for (; *s;) {
    7741            0 :             if (*s == DIR_SEPARATOR)
    7742            0 :                break;
    7743            0 :             envname += *s;
    7744            0 :             s++;
    7745              :          }
    7746            0 :          const char *e = getenv(envname.c_str());
    7747              :          //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
    7748            0 :          if (!e) {
    7749              :             //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
    7750            0 :             r += '$';
    7751            0 :             r += envname;
    7752              :          } else {
    7753            0 :             r += e;
    7754              :             //if (r[r.length()-1] != DIR_SEPARATOR)
    7755              :             //r += DIR_SEPARATOR_STR;
    7756              :          }
    7757            0 :       } else {
    7758            0 :          r += *s;
    7759            0 :          s++;
    7760              :       }
    7761              :    }
    7762            0 :    return r;
    7763            0 : }
    7764              : 
    7765            0 : static bool test_cm_expand_env1(const char *str, const char *expected) {
    7766            0 :    std::string s = cm_expand_env(str);
    7767            0 :    printf("test_expand_env: [%s] -> [%s] expected [%s]",
    7768              :           str,
    7769              :           s.c_str(),
    7770              :           expected);
    7771            0 :    if (s != expected) {
    7772            0 :       printf(", MISMATCH!\n");
    7773            0 :       return false;
    7774              :    }
    7775              : 
    7776            0 :    printf("\n");
    7777            0 :    return true;
    7778            0 : }
    7779              : 
    7780            0 : void cm_test_expand_env() {
    7781            0 :    printf("Test expand_end()\n");
    7782            0 :    setenv("FOO", "foo", 1);
    7783            0 :    setenv("BAR", "bar", 1);
    7784            0 :    setenv("EMPTY", "", 1);
    7785            0 :    unsetenv("UNDEF");
    7786              : 
    7787            0 :    bool ok = true;
    7788              : 
    7789            0 :    ok &= test_cm_expand_env1("aaa", "aaa");
    7790            0 :    ok &= test_cm_expand_env1("$FOO", "foo");
    7791            0 :    ok &= test_cm_expand_env1("/$FOO", "/foo");
    7792            0 :    ok &= test_cm_expand_env1("/$FOO/", "/foo/");
    7793            0 :    ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
    7794            0 :    ok &= test_cm_expand_env1("$FOO1", "$FOO1");
    7795            0 :    ok &= test_cm_expand_env1("1$FOO", "1foo");
    7796            0 :    ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
    7797            0 :    ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
    7798              : 
    7799            0 :    if (ok) {
    7800            0 :       printf("test_expand_env: all tests passed!\n");
    7801              :    } else {
    7802            0 :       printf("test_expand_env: test FAILED!\n");
    7803              :    }
    7804            0 : }
    7805              : 
    7806              : /**dox***************************************************************/
    7807              : 
    7808              : /** @} *//* end of cmfunctionc */
    7809              : 
    7810              : /**dox***************************************************************/
    7811              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    7812              : 
    7813              : /********************************************************************/
    7814            0 : INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
    7815              : /********************************************************************\
    7816              : 
    7817              :   Routine: bm_buffer_info
    7818              : 
    7819              :   Purpose: Copies the current buffer header referenced by buffer_handle
    7820              :            into the *buffer_header structure which must be supplied
    7821              :            by the calling routine.
    7822              : 
    7823              :   Input:
    7824              :     INT buffer_handle       Handle of the buffer to get the header from
    7825              : 
    7826              :   Output:
    7827              :     BUFFER_HEADER *buffer_header   Destination address which gets a copy
    7828              :                                    of the buffer header structure.
    7829              : 
    7830              :   Function value:
    7831              :     BM_SUCCESS              Successful completion
    7832              :     BM_INVALID_HANDLE       Buffer handle is invalid
    7833              :     RPC_NET_ERROR           Network error
    7834              : 
    7835              : \********************************************************************/
    7836              : {
    7837            0 :    if (rpc_is_remote())
    7838            0 :       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
    7839              : 
    7840              : #ifdef LOCAL_ROUTINES
    7841              : 
    7842            0 :    int status = 0;
    7843            0 :    BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
    7844              : 
    7845            0 :    if (!pbuf)
    7846            0 :       return status;
    7847              : 
    7848            0 :    bm_lock_buffer_guard pbuf_guard(pbuf);
    7849              : 
    7850            0 :    if (!pbuf_guard.is_locked())
    7851            0 :       return pbuf_guard.get_status();
    7852              : 
    7853            0 :    memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
    7854              : 
    7855              : #endif                          /* LOCAL_ROUTINES */
    7856              : 
    7857            0 :    return BM_SUCCESS;
    7858            0 : }
    7859              : 
    7860              : /********************************************************************/
    7861            0 : INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
    7862              : /********************************************************************\
    7863              : 
    7864              :   Routine: bm_get_buffer_level
    7865              : 
    7866              :   Purpose: Return number of bytes in buffer or in cache
    7867              : 
    7868              :   Input:
    7869              :     INT buffer_handle       Handle of the buffer to get the info
    7870              : 
    7871              :   Output:
    7872              :     INT *n_bytes              Number of bytes in buffer
    7873              : 
    7874              :   Function value:
    7875              :     BM_SUCCESS              Successful completion
    7876              :     BM_INVALID_HANDLE       Buffer handle is invalid
    7877              :     RPC_NET_ERROR           Network error
    7878              : 
    7879              : \********************************************************************/
    7880              : {
    7881            0 :    if (rpc_is_remote())
    7882            0 :       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
    7883              : 
    7884              : #ifdef LOCAL_ROUTINES
    7885              :    {
    7886            0 :       int status = 0;
    7887              : 
    7888            0 :       BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
    7889              : 
    7890            0 :       if (!pbuf)
    7891            0 :          return status;
    7892              : 
    7893            0 :       bm_lock_buffer_guard pbuf_guard(pbuf);
    7894              : 
    7895            0 :       if (!pbuf_guard.is_locked())
    7896            0 :          return pbuf_guard.get_status();
    7897              : 
    7898            0 :       BUFFER_HEADER *pheader = pbuf->buffer_header;
    7899              : 
    7900            0 :       BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
    7901              : 
    7902            0 :       *n_bytes = pheader->write_pointer - pclient->read_pointer;
    7903            0 :       if (*n_bytes < 0)
    7904            0 :          *n_bytes += pheader->size;
    7905              : 
    7906            0 :       pbuf_guard.unlock();
    7907              : 
    7908            0 :       if (pbuf->read_cache_size) {
    7909            0 :          status = bm_lock_buffer_read_cache(pbuf);
    7910            0 :          if (status == BM_SUCCESS) {
    7911              :             /* add bytes in cache */
    7912            0 :             if (pbuf->read_cache_wp > pbuf->read_cache_rp)
    7913            0 :                *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
    7914            0 :             pbuf->read_cache_mutex.unlock();
    7915              :          }
    7916              :       }
    7917            0 :    }
    7918              : #endif                          /* LOCAL_ROUTINES */
    7919              : 
    7920            0 :    return BM_SUCCESS;
    7921              : }
    7922              : 
    7923              : 
    7924              : #ifdef LOCAL_ROUTINES
    7925              : 
    7926              : /********************************************************************/
    7927            2 : static int bm_lock_buffer_read_cache(BUFFER *pbuf)
    7928              : {
    7929            2 :    bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
    7930              : 
    7931            2 :    if (!locked) {
    7932            0 :       fprintf(stderr, "bm_lock_buffer_read_cache: Error: Cannot lock read cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
    7933            0 :       cm_msg(MERROR, "bm_lock_buffer_read_cache", "Cannot lock read cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
    7934            0 :       abort();
    7935              :       /* DOES NOT RETURN */
    7936              :    }
    7937              : 
    7938            2 :    if (!pbuf->attached) {
    7939            0 :       pbuf->read_cache_mutex.unlock();
    7940            0 :       fprintf(stderr, "bm_lock_buffer_read_cache: Error: Cannot lock read cache of buffer \"%s\", buffer was closed while we waited for the buffer_mutex\n", pbuf->buffer_name);
    7941            0 :       return BM_INVALID_HANDLE;
    7942              :    }
    7943              : 
    7944            2 :    return BM_SUCCESS;
    7945              : }
    7946              : 
    7947              : /********************************************************************/
    7948            2 : static int bm_lock_buffer_write_cache(BUFFER *pbuf)
    7949              : {
    7950            2 :    bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
    7951              : 
    7952            2 :    if (!locked) {
    7953            0 :       fprintf(stderr, "bm_lock_buffer_write_cache: Error: Cannot lock write cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
    7954            0 :       cm_msg(MERROR, "bm_lock_buffer_write_cache", "Cannot lock write cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
    7955            0 :       abort();
    7956              :       /* DOES NOT RETURN */
    7957              :    }
    7958              : 
    7959            2 :    if (!pbuf->attached) {
    7960            0 :       pbuf->write_cache_mutex.unlock();
    7961            0 :       fprintf(stderr, "bm_lock_buffer_write_cache: Error: Cannot lock write cache of buffer \"%s\", buffer was closed while we waited for the buffer_mutex\n", pbuf->buffer_name);
    7962            0 :       return BM_INVALID_HANDLE;
    7963              :    }
    7964              : 
    7965            2 :    return BM_SUCCESS;
    7966              : }
    7967              : 
    7968              : /********************************************************************/
    7969           25 : static int bm_lock_buffer_mutex(BUFFER *pbuf)
    7970              : {
    7971              :    //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
    7972              : 
    7973           25 :    bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
    7974              : 
    7975           25 :    if (!locked) {
    7976            0 :       fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
    7977            0 :       cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
    7978            0 :       abort();
    7979              :       /* DOES NOT RETURN */
    7980              :    }
    7981              : 
    7982           25 :    if (!pbuf->attached) {
    7983            0 :       pbuf->buffer_mutex.unlock();
    7984            0 :       fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", buffer was closed while we waited for the buffer_mutex\n", pbuf->buffer_name);
    7985            0 :       return BM_INVALID_HANDLE;
    7986              :    }
    7987              : 
    7988              :    //static int counter = 0;
    7989              :    //counter++;
    7990              :    //printf("locked %d!\n", counter);
    7991              :    //if (counter > 50)
    7992              :    //   ::sleep(3);
    7993              : 
    7994           25 :    return BM_SUCCESS;
    7995              : }
    7996              : 
    7997              : /********************************************************************/
    7998           25 : static int xbm_lock_buffer(BUFFER *pbuf)
    7999              : {
    8000              :    int status;
    8001              : 
    8002              :    // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
    8003              : 
    8004              :    //if (pbuf->locked) {
    8005              :    //   fprintf(stderr, "double lock, abort!\n");
    8006              :    //   abort();
    8007              :    //}
    8008              : 
    8009           25 :    status = bm_lock_buffer_mutex(pbuf);
    8010              : 
    8011           25 :    if (status != BM_SUCCESS)
    8012            0 :       return status;
    8013              : 
    8014           25 :    status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
    8015              :  
    8016           25 :    if (status != SS_SUCCESS) {
    8017            0 :       fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
    8018              : 
    8019            0 :       status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
    8020              : 
    8021            0 :       if (status != SS_SUCCESS) {
    8022            0 :          fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 10 seconds, buffer semaphore is probably stuck, delete %s.SHM and try again!\n", pbuf->buffer_name, pbuf->buffer_name);
    8023              : 
    8024            0 :          if (pbuf->buffer_header) {
    8025            0 :             for (int i=0; i<MAX_CLIENTS; i++) {
    8026            0 :                fprintf(stderr, "bm_lock_buffer: Buffer \"%s\" client %d \"%s\" pid %d\n", pbuf->buffer_name, i, pbuf->buffer_header->client[i].name, pbuf->buffer_header->client[i].pid);
    8027              :             }
    8028              :          }
    8029              :             
    8030            0 :          status = ss_semaphore_wait_for(pbuf->semaphore, _bm_lock_timeout);
    8031              :          
    8032            0 :          if (status != SS_SUCCESS) {
    8033            0 :             fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
    8034            0 :             cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
    8035            0 :             abort();
    8036              :             /* DOES NOT RETURN */
    8037              :          }
    8038              :       }
    8039              :    }
    8040              : 
    8041              :    // protect against double lock
    8042           25 :    assert(!pbuf->locked);
    8043           25 :    pbuf->locked = TRUE;
    8044              : 
    8045              : #if 0
    8046              :    int x = MAX_CLIENTS - 1;
    8047              :    if (pbuf->buffer_header->client[x].unused1 != 0) {
    8048              :       printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
    8049              :    }
    8050              :    //assert(pbuf->buffer_header->client[x].unused1 == 0);
    8051              :    pbuf->buffer_header->client[x].unused1 = getpid();
    8052              : #endif
    8053              : 
    8054           25 :    pbuf->count_lock++;
    8055              : 
    8056           25 :    return BM_SUCCESS;
    8057              : }
    8058              : 
    8059              : /********************************************************************/
    8060           25 : static void xbm_unlock_buffer(BUFFER *pbuf) {
    8061              :    // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
    8062              : 
    8063              : #if 0
    8064              :    int x = MAX_CLIENTS-1;
    8065              :    if (pbuf->attached) {
    8066              :       if (pbuf->buffer_header->client[x].unused1 != getpid()) {
    8067              :          printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
    8068              :       }
    8069              :       pbuf->buffer_header->client[x].unused1 = 0;
    8070              :    } else {
    8071              :       printf("unlock [??????] unused1 ????? pid %d\n", getpid());
    8072              :    }
    8073              : #endif
    8074              : 
    8075              :    // protect against double unlock
    8076           25 :    assert(pbuf->locked);
    8077           25 :    pbuf->locked = FALSE;
    8078              : 
    8079           25 :    ss_semaphore_release(pbuf->semaphore);
    8080           25 :    pbuf->buffer_mutex.unlock();
    8081           25 : }
    8082              : 
    8083              : #endif                          /* LOCAL_ROUTINES */
    8084              : 
    8085              : /********************************************************************/
    8086            2 : INT bm_init_buffer_counters(INT buffer_handle)
    8087              : /********************************************************************\
    8088              : 
    8089              :   Routine: bm_init_event_counters
    8090              : 
    8091              :   Purpose: Initialize counters for a specific buffer. This routine
    8092              :            should be called at the beginning of a run.
    8093              : 
    8094              :   Input:
    8095              :     INT    buffer_handle    Handle to the buffer to be
    8096              :                             initialized.
    8097              :   Output:
    8098              :     none
    8099              : 
    8100              :   Function value:
    8101              :     BM_SUCCESS              Successful completion
    8102              :     BM_INVALID_HANDLE       Buffer handle is invalid
    8103              : 
    8104              : \********************************************************************/
    8105              : {
    8106            2 :    if (rpc_is_remote())
    8107            0 :       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
    8108              : 
    8109              : #ifdef LOCAL_ROUTINES
    8110              : 
    8111            2 :    int status = 0;
    8112              : 
    8113            2 :    BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
    8114              : 
    8115            2 :    if (!pbuf)
    8116            0 :       return status;
    8117              : 
    8118            2 :    bm_lock_buffer_guard pbuf_guard(pbuf);
    8119              : 
    8120            2 :    if (!pbuf_guard.is_locked())
    8121            0 :       return pbuf_guard.get_status();
    8122              : 
    8123            2 :    pbuf->buffer_header->num_in_events = 0;
    8124            2 :    pbuf->buffer_header->num_out_events = 0;
    8125              : 
    8126              : #endif                          /* LOCAL_ROUTINES */
    8127              : 
    8128            2 :    return BM_SUCCESS;
    8129            2 : }
    8130              : 
    8131              : /**dox***************************************************************/
    8132              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    8133              : 
    8134              : /**dox***************************************************************/
    8135              : /** @addtogroup bmfunctionc
    8136              :  *
    8137              :  *  @{  */
    8138              : 
    8139              : /********************************************************************/
    8140              : /**
    8141              : Modifies buffer cache size.
    8142              : Without a buffer cache, events are copied to/from the shared
    8143              : memory event by event.
    8144              : 
    8145              : To protect processed from accessing the shared memory simultaneously,
    8146              : semaphores are used. Since semaphore operations are CPU consuming (typically
    8147              : 50-100us) this can slow down the data transfer especially for small events.
    8148              : By using a cache the number of semaphore operations is reduced dramatically.
    8149              : Instead writing directly to the shared memory, the events are copied to a
    8150              : local cache buffer. When this buffer is full, it is copied to the shared
    8151              : memory in one operation. The same technique can be used when receiving events.
    8152              : 
    8153              : The drawback of this method is that the events have to be copied twice, once to the
    8154              : cache and once from the cache to the shared memory. Therefore it can happen that the
    8155              : usage of a cache even slows down data throughput on a given environment (computer
    8156              : type, OS type, event size).
    8157              : The cache size has therefore be optimized manually to maximize data throughput.
    8158              : @param buffer_handle buffer handle obtained via bm_open_buffer()
    8159              : @param read_size cache size for reading events in bytes, zero for no cache
    8160              : @param write_size cache size for writing events in bytes, zero for no cache
    8161              : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
    8162              : */
    8163            0 : INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
    8164              : /*------------------------------------------------------------------*/
    8165              : {
    8166            0 :    if (rpc_is_remote())
    8167            0 :       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
    8168              : 
    8169              : #ifdef LOCAL_ROUTINES
    8170              :    {
    8171            0 :       int status = 0;
    8172              : 
    8173            0 :       BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
    8174              : 
    8175            0 :       if (!pbuf)
    8176            0 :          return status;
    8177              : 
    8178              :       /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
    8179              : 
    8180            0 :       status = bm_lock_buffer_mutex(pbuf);
    8181              : 
    8182            0 :       if (status != BM_SUCCESS)
    8183            0 :          return status;
    8184              : 
    8185              :       if (write_size < 0)
    8186              :          write_size = 0;
    8187              : 
    8188            0 :       if (write_size > 0) {
    8189            0 :          if (write_size < MIN_WRITE_CACHE_SIZE) {
    8190            0 :             cm_msg(MERROR, "bm_set_cache_size", "requested write cache size %zu on buffer \"%s\" too small, will use minimum size %d", write_size, pbuf->buffer_name, MIN_WRITE_CACHE_SIZE);
    8191            0 :             write_size = MIN_WRITE_CACHE_SIZE;
    8192              :          }
    8193              :       }
    8194              : 
    8195            0 :       size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
    8196              : 
    8197            0 :       if (write_size > max_write_size) {
    8198            0 :          size_t new_write_size = max_write_size;
    8199            0 :          cm_msg(MERROR, "bm_set_cache_size", "requested write cache size %zu on buffer \"%s\" is too big: buffer size is %d, write cache size will be %zu bytes", write_size, pbuf->buffer_name, pbuf->buffer_header->size, new_write_size);
    8200            0 :          write_size = new_write_size;
    8201              :       }
    8202              : 
    8203            0 :       pbuf->buffer_mutex.unlock();
    8204              : 
    8205              :       /* resize read cache */
    8206              : 
    8207            0 :       status = bm_lock_buffer_read_cache(pbuf);
    8208              : 
    8209            0 :       if (status != BM_SUCCESS) {
    8210            0 :          return status;
    8211              :       }
    8212              : 
    8213            0 :       if (pbuf->read_cache_size > 0) {
    8214            0 :          free(pbuf->read_cache);
    8215            0 :          pbuf->read_cache = NULL;
    8216              :       }
    8217              : 
    8218            0 :       if (read_size > 0) {
    8219            0 :          pbuf->read_cache = (char *) malloc(read_size);
    8220            0 :          if (pbuf->read_cache == NULL) {
    8221            0 :             pbuf->read_cache_size = 0;
    8222            0 :             pbuf->read_cache_rp = 0;
    8223            0 :             pbuf->read_cache_wp = 0;
    8224            0 :             pbuf->read_cache_mutex.unlock();
    8225            0 :             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate read cache for buffer \"%s\", malloc(%zu) failed", pbuf->buffer_name, read_size);
    8226            0 :             return BM_NO_MEMORY;
    8227              :          }
    8228              :       }
    8229              : 
    8230            0 :       pbuf->read_cache_size = read_size;
    8231            0 :       pbuf->read_cache_rp = 0;
    8232            0 :       pbuf->read_cache_wp = 0;
    8233              : 
    8234            0 :       pbuf->read_cache_mutex.unlock();
    8235              : 
    8236              :       /* resize the write cache */
    8237              : 
    8238            0 :       status = bm_lock_buffer_write_cache(pbuf);
    8239              : 
    8240            0 :       if (status != BM_SUCCESS)
    8241            0 :          return status;
    8242              : 
    8243              :       // FIXME: should flush the write cache!
    8244            0 :       if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
    8245            0 :          cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
    8246              :       }
    8247              : 
    8248              :       /* manage write cache */
    8249            0 :       if (pbuf->write_cache_size > 0) {
    8250            0 :          free(pbuf->write_cache);
    8251            0 :          pbuf->write_cache = NULL;
    8252              :       }
    8253              : 
    8254            0 :       if (write_size > 0) {
    8255            0 :          pbuf->write_cache = (char *) M_MALLOC(write_size);
    8256            0 :          if (pbuf->write_cache == NULL) {
    8257            0 :             pbuf->write_cache_size = 0;
    8258            0 :             pbuf->write_cache_rp = 0;
    8259            0 :             pbuf->write_cache_wp = 0;
    8260            0 :             pbuf->write_cache_mutex.unlock();
    8261            0 :             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate write cache for buffer \"%s\", malloc(%zu) failed", pbuf->buffer_name, write_size);
    8262            0 :             return BM_NO_MEMORY;
    8263              :          }
    8264              :       }
    8265              : 
    8266            0 :       pbuf->write_cache_size = write_size;
    8267            0 :       pbuf->write_cache_rp = 0;
    8268            0 :       pbuf->write_cache_wp = 0;
    8269              : 
    8270            0 :       pbuf->write_cache_mutex.unlock();
    8271              :    }
    8272              : #endif                          /* LOCAL_ROUTINES */
    8273              : 
    8274            0 :    return BM_SUCCESS;
    8275              : }
    8276              : 
    8277              : /********************************************************************/
    8278              : /**
    8279              : Compose a Midas event header.
    8280              : An event header can usually be set-up manually or
    8281              : through this routine. If the data size of the event is not known when
    8282              : the header is composed, it can be set later with event_header->data-size = <...>
    8283              : Following structure is created at the beginning of an event
    8284              : \code
    8285              : typedef struct {
    8286              :  short int     event_id;
    8287              :  short int     trigger_mask;
    8288              :  DWORD         serial_number;
    8289              :  DWORD         time_stamp;
    8290              :  DWORD         data_size;
    8291              : } EVENT_HEADER;
    8292              : 
    8293              : char event[1000];
    8294              :  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
    8295              :  *(event+sizeof(EVENT_HEADER)) = <...>
    8296              : \endcode
    8297              : @param event_header pointer to the event header
    8298              : @param event_id event ID of the event
    8299              : @param trigger_mask trigger mask of the event
    8300              : @param data_size size if the data part of the event in bytes
    8301              : @param serial serial number
    8302              : @return BM_SUCCESS
    8303              : */
    8304           11 : INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
    8305              : {
    8306           11 :    event_header->event_id = event_id;
    8307           11 :    event_header->trigger_mask = trigger_mask;
    8308           11 :    event_header->data_size = data_size;
    8309           11 :    event_header->time_stamp = ss_time();
    8310           11 :    event_header->serial_number = serial;
    8311              : 
    8312           11 :    return BM_SUCCESS;
    8313              : }
    8314              : 
    8315            0 : INT bm_compose_event_threadsafe(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD *serial)
    8316              : {
    8317              :    static std::mutex mutex;
    8318              : 
    8319            0 :    event_header->event_id = event_id;
    8320            0 :    event_header->trigger_mask = trigger_mask;
    8321            0 :    event_header->data_size = data_size;
    8322            0 :    event_header->time_stamp = ss_time();
    8323              :    {
    8324            0 :       std::lock_guard<std::mutex> lock(mutex);
    8325            0 :       event_header->serial_number = *serial;
    8326            0 :       *serial = *serial + 1;
    8327              :       // implicit unlock
    8328            0 :    }
    8329              : 
    8330            0 :    return BM_SUCCESS;
    8331              : }
    8332              : 
    8333              : /**dox***************************************************************/
    8334              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    8335              : 
    8336              : /********************************************************************/
    8337            0 : INT bm_add_event_request(INT buffer_handle, short int event_id,
    8338              :                          short int trigger_mask,
    8339              :                          INT sampling_type,
    8340              :                          EVENT_HANDLER *func,
    8341              :                          INT request_id)
    8342              : /********************************************************************\
    8343              : 
    8344              :   Routine:  bm_add_event_request
    8345              : 
    8346              :   Purpose:  Place a request for a specific event type in the client
    8347              :             structure of the buffer refereced by buffer_handle.
    8348              : 
    8349              :   Input:
    8350              :     INT          buffer_handle  Handle to the buffer where the re-
    8351              :                                 quest should be placed in
    8352              : 
    8353              :     short int    event_id       Event ID      \
    8354              :     short int    trigger_mask   Trigger mask  / Event specification
    8355              : 
    8356              :     INT          sampling_type  One of GET_ALL, GET_NONBLOCKING or GET_RECENT
    8357              : 
    8358              : 
    8359              :                  Note: to request all types of events, use
    8360              :                    event_id = 0 (all others should be !=0 !)
    8361              :                    trigger_mask = TRIGGER_ALL
    8362              :                    sampling_typ = GET_ALL
    8363              : 
    8364              : 
    8365              :     void         *func          Callback function
    8366              :     INT          request_id     Request id (unique number assigned
    8367              :                                 by bm_request_event)
    8368              : 
    8369              :   Output:
    8370              :     none
    8371              : 
    8372              :   Function value:
    8373              :     BM_SUCCESS              Successful completion
    8374              :     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
    8375              :                             MIDAS.H should be increased.
    8376              :     BM_INVALID_HANDLE       Buffer handle is invalid
    8377              :     BM_INVALID_PARAM        GET_RECENT is used with non-zero cache size
    8378              :     RPC_NET_ERROR           Network error
    8379              : 
    8380              : \********************************************************************/
    8381              : {
    8382            0 :    if (rpc_is_remote())
    8383            0 :       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
    8384            0 :                       trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
    8385              : 
    8386              : #ifdef LOCAL_ROUTINES
    8387              :    {
    8388            0 :       int status = 0;
    8389              : 
    8390            0 :       BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
    8391              : 
    8392            0 :       if (!pbuf)
    8393            0 :          return status;
    8394              : 
    8395              :       /* lock buffer */
    8396            0 :       bm_lock_buffer_guard pbuf_guard(pbuf);
    8397              : 
    8398            0 :       if (!pbuf_guard.is_locked())
    8399            0 :          return pbuf_guard.get_status();
    8400              : 
    8401              :       /* avoid callback/non callback requests */
    8402            0 :       if (func == NULL && pbuf->callback) {
    8403            0 :          pbuf_guard.unlock(); // unlock before cm_msg()
    8404            0 :          cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
    8405            0 :          return BM_INVALID_MIXING;
    8406              :       }
    8407              : 
    8408              :       /* do not allow GET_RECENT with nonzero cache size */
    8409            0 :       if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
    8410            0 :          pbuf_guard.unlock(); // unlock before cm_msg()
    8411            0 :          cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
    8412            0 :          return BM_INVALID_PARAM;
    8413              :       }
    8414              : 
    8415              :       /* get a pointer to the proper client structure */
    8416            0 :       BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
    8417              : 
    8418              :       /* look for a empty request entry */
    8419              :       int i;
    8420            0 :       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
    8421            0 :          if (!pclient->event_request[i].valid)
    8422            0 :             break;
    8423              : 
    8424            0 :       if (i == MAX_EVENT_REQUESTS) {
    8425              :          // implicit unlock
    8426            0 :          return BM_NO_MEMORY;
    8427              :       }
    8428              : 
    8429              :       /* setup event_request structure */
    8430            0 :       pclient->event_request[i].id = request_id;
    8431            0 :       pclient->event_request[i].valid = TRUE;
    8432            0 :       pclient->event_request[i].event_id = event_id;
    8433            0 :       pclient->event_request[i].trigger_mask = trigger_mask;
    8434            0 :       pclient->event_request[i].sampling_type = sampling_type;
    8435              : 
    8436            0 :       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
    8437              : 
    8438            0 :       pbuf->get_all_flag = pclient->all_flag;
    8439              : 
    8440              :       /* set callback flag in buffer structure */
    8441            0 :       if (func != NULL)
    8442            0 :          pbuf->callback = TRUE;
    8443              : 
    8444              :       /*
    8445              :          Save the index of the last request in the list so that later only the
    8446              :          requests 0..max_request_index-1 have to be searched through.
    8447              :        */
    8448              : 
    8449            0 :       if (i + 1 > pclient->max_request_index)
    8450            0 :          pclient->max_request_index = i + 1;
    8451            0 :    }
    8452              : #endif                          /* LOCAL_ROUTINES */
    8453              : 
    8454            0 :    return BM_SUCCESS;
    8455              : }
    8456              : 
    8457              : /**dox***************************************************************/
    8458              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    8459              : 
    8460              : /********************************************************************/
    8461              : /**
    8462              : Place an event request based on certain characteristics.
    8463              : Multiple event requests can be placed for each buffer, which
    8464              : are later identified by their request ID. They can contain different callback
    8465              : routines. Example see bm_open_buffer() and bm_receive_event()
    8466              : @param buffer_handle buffer handle obtained via bm_open_buffer()
    8467              : @param event_id event ID for requested events. Use EVENTID_ALL
    8468              : to receive events with any ID.
    8469              : @param trigger_mask trigger mask for requested events.
    8470              : The requested events must have at least one bit in its
    8471              : trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
    8472              : receive events with any trigger mask.
    8473              : @param sampling_type specifies how many events to receive.
    8474              : A value of GET_ALL receives all events which
    8475              : match the specified event ID and trigger mask. If the events are consumed slower
    8476              : than produced, the producer is automatically slowed down. A value of GET_NONBLOCKING
    8477              : receives as much events as possible without slowing down the producer. GET_ALL is
    8478              : typically used by the logger, while GET_NONBLOCKING is typically used by analyzers.
    8479              : @param request_id request ID returned by the function.
    8480              : This ID is passed to the callback routine and must
    8481              : be used in the bm_delete_request() routine.
    8482              : @param func allback routine which gets called when an event of the
    8483              : specified type is received.
    8484              : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
    8485              : BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
    8486              : should be increased.
    8487              : */
    8488            0 : INT bm_request_event(HNDLE buffer_handle, short int event_id,
    8489              :                      short int trigger_mask,
    8490              :                      INT sampling_type, HNDLE *request_id,
    8491              :                      EVENT_HANDLER *func)
    8492              : {
    8493            0 :    assert(request_id != NULL);
    8494              :    
    8495            0 :    EventRequest r;
    8496            0 :    r.buffer_handle = buffer_handle;
    8497            0 :    r.event_id      = event_id;
    8498            0 :    r.trigger_mask  = trigger_mask;
    8499            0 :    r.dispatcher    = func;
    8500              : 
    8501              :    {
    8502            0 :       std::lock_guard<std::mutex> guard(_request_list_mutex);
    8503              :    
    8504            0 :       bool found = false;
    8505              :       
    8506              :       // find deleted entry
    8507            0 :       for (size_t i = 0; i < _request_list.size(); i++) {
    8508            0 :          if (_request_list[i].buffer_handle == 0) {
    8509            0 :             _request_list[i] = r;
    8510            0 :             *request_id = i;
    8511            0 :             found = true;
    8512            0 :             break;
    8513              :          }
    8514              :       }
    8515              :       
    8516            0 :       if (!found) { // not found
    8517            0 :          *request_id = _request_list.size();
    8518            0 :          _request_list.push_back(r);
    8519              :       }
    8520              : 
    8521              :       // implicit unlock()
    8522            0 :    }
    8523              : 
    8524              :    /* add request in buffer structure */
    8525            0 :    int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
    8526            0 :    if (status != BM_SUCCESS)
    8527            0 :       return status;
    8528              : 
    8529            0 :    return BM_SUCCESS;
    8530              : }
    8531              : 
    8532              : /********************************************************************/
    8533              : /**
    8534              : Delete a previously placed request for a specific event
    8535              : type in the client structure of the buffer refereced by buffer_handle.
    8536              : @param buffer_handle  Handle to the buffer where the re-
    8537              :                                 quest should be placed in
    8538              : @param request_id     Request id returned by bm_request_event
    8539              : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR
    8540              : */
    8541            0 : INT bm_remove_event_request(INT buffer_handle, INT request_id) {
    8542            0 :    if (rpc_is_remote())
    8543            0 :       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
    8544              : 
    8545              : #ifdef LOCAL_ROUTINES
    8546              :    {
    8547            0 :       int status = 0;
    8548              : 
    8549            0 :       BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
    8550              : 
    8551            0 :       if (!pbuf)
    8552            0 :          return status;
    8553              : 
    8554              :       /* lock buffer */
    8555            0 :       bm_lock_buffer_guard pbuf_guard(pbuf);
    8556              : 
    8557            0 :       if (!pbuf_guard.is_locked())
    8558            0 :          return pbuf_guard.get_status();
    8559              : 
    8560              :       INT i, deleted;
    8561              : 
    8562              :       /* get a pointer to the proper client structure */
    8563            0 :       BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
    8564              : 
    8565              :       /* check all requests and set to zero if matching */
    8566            0 :       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
    8567            0 :          if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
    8568            0 :             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
    8569            0 :             deleted++;
    8570              :          }
    8571              : 
    8572              :       /* calculate new max_request_index entry */
    8573            0 :       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
    8574            0 :          if (pclient->event_request[i].valid)
    8575            0 :             break;
    8576              : 
    8577            0 :       pclient->max_request_index = i + 1;
    8578              : 
    8579              :       /* calculate new all_flag */
    8580            0 :       pclient->all_flag = FALSE;
    8581              : 
    8582            0 :       for (i = 0; i < pclient->max_request_index; i++)
    8583            0 :          if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
    8584            0 :             pclient->all_flag = TRUE;
    8585            0 :             break;
    8586              :          }
    8587              : 
    8588            0 :       pbuf->get_all_flag = pclient->all_flag;
    8589              : 
    8590            0 :       if (!deleted)
    8591            0 :          return BM_NOT_FOUND;
    8592            0 :    }
    8593              : #endif                          /* LOCAL_ROUTINES */
    8594              : 
    8595            0 :    return BM_SUCCESS;
    8596              : }
    8597              : 
    8598              : /********************************************************************/
    8599              : /**
    8600              : Deletes an event request previously done with bm_request_event().
    8601              : When an event request gets deleted, events of that requested type are
    8602              : not received any more. When a buffer is closed via bm_close_buffer(), all
    8603              : event requests from that buffer are deleted automatically
    8604              : @param request_id request identifier given by bm_request_event()
    8605              : @return BM_SUCCESS, BM_INVALID_HANDLE
    8606              : */
    8607            0 : INT bm_delete_request(INT request_id)
    8608              : {
    8609            0 :    _request_list_mutex.lock();
    8610              :    
    8611            0 :    if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
    8612            0 :       _request_list_mutex.unlock();
    8613            0 :       return BM_INVALID_HANDLE;
    8614              :    }
    8615              : 
    8616            0 :    int buffer_handle = _request_list[request_id].buffer_handle;
    8617              : 
    8618            0 :    _request_list[request_id].clear();
    8619              : 
    8620            0 :    _request_list_mutex.unlock();
    8621              : 
    8622              :    /* remove request entry from buffer */
    8623            0 :    return bm_remove_event_request(buffer_handle, request_id);
    8624              : }
    8625              : 
    8626              : #if 0                           // currently not used
    8627              : static void bm_show_pointers(const BUFFER_HEADER * pheader)
    8628              : {
    8629              :    int i;
    8630              :    const BUFFER_CLIENT *pclient;
    8631              : 
    8632              :    pclient = pheader->client;
    8633              : 
    8634              :    printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
    8635              :           pheader->write_pointer, pheader->size);
    8636              :    for (i = 0; i < pheader->max_client_index; i++)
    8637              :       if (pclient[i].pid) {
    8638              :          printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
    8639              :       }
    8640              : 
    8641              :    printf("done\n");
    8642              : }
    8643              : #endif
    8644              : 
    8645            0 : static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient) {
    8646            0 :    assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
    8647            0 :    assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
    8648              : 
    8649            0 :    if (pheader->read_pointer <= pheader->write_pointer) {
    8650              : 
    8651            0 :       if (pclient->read_pointer < pheader->read_pointer) {
    8652            0 :          cm_msg(MINFO, "bm_validate_client_pointers",
    8653              :                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
    8654            0 :                 pclient->name,
    8655            0 :                 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
    8656              : 
    8657            0 :          pclient->read_pointer = pheader->read_pointer;
    8658              :       }
    8659              : 
    8660            0 :       if (pclient->read_pointer > pheader->write_pointer) {
    8661            0 :          cm_msg(MINFO, "bm_validate_client_pointers",
    8662              :                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
    8663            0 :                 pclient->name,
    8664            0 :                 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
    8665              : 
    8666            0 :          pclient->read_pointer = pheader->write_pointer;
    8667              :       }
    8668              : 
    8669              :    } else {
    8670              : 
    8671            0 :       if (pclient->read_pointer < 0) {
    8672            0 :          cm_msg(MINFO, "bm_validate_client_pointers",
    8673              :                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
    8674            0 :                 pclient->name,
    8675            0 :                 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
    8676              : 
    8677            0 :          pclient->read_pointer = pheader->read_pointer;
    8678              :       }
    8679              : 
    8680            0 :       if (pclient->read_pointer >= pheader->size) {
    8681            0 :          cm_msg(MINFO, "bm_validate_client_pointers",
    8682              :                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
    8683            0 :                 pclient->name,
    8684            0 :                 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
    8685              : 
    8686            0 :          pclient->read_pointer = pheader->read_pointer;
    8687              :       }
    8688              : 
    8689            0 :       if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
    8690            0 :          cm_msg(MINFO, "bm_validate_client_pointers",
    8691              :                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
    8692            0 :                 pclient->name,
    8693            0 :                 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
    8694              : 
    8695            0 :          pclient->read_pointer = pheader->read_pointer;
    8696              :       }
    8697              :    }
    8698            0 : }
    8699              : 
    8700              : #if 0                           // currently not used
    8701              : static void bm_validate_pointers(BUFFER_HEADER * pheader)
    8702              : {
    8703              :    BUFFER_CLIENT *pclient = pheader->client;
    8704              :    int i;
    8705              : 
    8706              :    for (i = 0; i < pheader->max_client_index; i++)
    8707              :       if (pclient[i].pid) {
    8708              :          bm_validate_client_pointers(pheader, &pclient[i]);
    8709              :       }
    8710              : }
    8711              : #endif
    8712              : 
    8713              : //
    8714              : // Buffer pointers
    8715              : //
    8716              : // normal:
    8717              : //
    8718              : // zero -->
    8719              : // ... free space
    8720              : // read_pointer -->
    8721              : // client1 rp -->
    8722              : // client2 rp -->
    8723              : // ... buffered data
    8724              : // write_pointer -->
    8725              : // ... free space
    8726              : // pheader->size -->
    8727              : //
    8728              : // inverted:
    8729              : //
    8730              : // zero -->
    8731              : // client3 rp -->
    8732              : // ... buffered data
    8733              : // client4 rp -->
    8734              : // write_pointer -->
    8735              : // ... free space
    8736              : // read_pointer -->
    8737              : // client1 rp -->
    8738              : // client2 rp -->
    8739              : // ... buffered data
    8740              : // pheader->size -->
    8741              : //
    8742              : 
    8743            0 : static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader) {
    8744            0 :    assert(caller_name);
    8745              : 
    8746              :    /* calculate global read pointer as "minimum" of client read pointers */
    8747            0 :    int min_rp = pheader->write_pointer;
    8748              : 
    8749              :    int i;
    8750            0 :    for (i = 0; i < pheader->max_client_index; i++) {
    8751            0 :       BUFFER_CLIENT *pc = pheader->client + i;
    8752            0 :       if (pc->pid) {
    8753            0 :          bm_validate_client_pointers_locked(pheader, pc);
    8754              : 
    8755              : #if 0
    8756              :          printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
    8757              :                 pheader->name,
    8758              :                 pheader->read_pointer,
    8759              :                 pheader->write_pointer,
    8760              :                 pheader->size,
    8761              :                 min_rp,
    8762              :                 pc->name,
    8763              :                 pc->read_pointer);
    8764              : #endif
    8765              : 
    8766            0 :          if (pheader->read_pointer <= pheader->write_pointer) {
    8767              :             // normal pointers
    8768            0 :             if (pc->read_pointer < min_rp)
    8769            0 :                min_rp = pc->read_pointer;
    8770              :          } else {
    8771              :             // inverted pointers
    8772            0 :             if (pc->read_pointer <= pheader->write_pointer) {
    8773              :                // clients 3 and 4
    8774            0 :                if (pc->read_pointer < min_rp)
    8775            0 :                   min_rp = pc->read_pointer;
    8776              :             } else {
    8777              :                // clients 1 and 2
    8778            0 :                int xptr = pc->read_pointer - pheader->size;
    8779            0 :                if (xptr < min_rp)
    8780            0 :                   min_rp = xptr;
    8781              :             }
    8782              :          }
    8783              :       }
    8784              :    }
    8785              : 
    8786            0 :    if (min_rp < 0)
    8787            0 :       min_rp += pheader->size;
    8788              : 
    8789            0 :    assert(min_rp >= 0);
    8790            0 :    assert(min_rp < pheader->size);
    8791              : 
    8792            0 :    if (min_rp == pheader->read_pointer) {
    8793            0 :       return FALSE;
    8794              :    }
    8795              : 
    8796              : #if 0
    8797              :    printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
    8798              :           pheader->name,
    8799              :           pheader->read_pointer,
    8800              :           pheader->write_pointer,
    8801              :           pheader->size,
    8802              :           min_rp);
    8803              : #endif
    8804              : 
    8805            0 :    pheader->read_pointer = min_rp;
    8806              : 
    8807            0 :    return TRUE;
    8808              : }
    8809              : 
    8810            0 : static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
    8811              :    int i;
    8812            0 :    int have_get_all_requests = 0;
    8813              : 
    8814            0 :    for (i = 0; i < pc->max_request_index; i++)
    8815            0 :       if (pc->event_request[i].valid)
    8816            0 :          have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
    8817              : 
    8818              :    /* only GET_ALL requests actually free space in the event buffer */
    8819            0 :    if (!have_get_all_requests)
    8820            0 :       return;
    8821              : 
    8822              :    /*
    8823              :       If read pointer has been changed, it may have freed up some space
    8824              :       for waiting producers. So check if free space is now more than 50%
    8825              :       of the buffer size and wake waiting producers.
    8826              :     */
    8827              : 
    8828            0 :    int free_space = pc->read_pointer - pheader->write_pointer;
    8829            0 :    if (free_space <= 0)
    8830            0 :       free_space += pheader->size;
    8831              : 
    8832            0 :    if (free_space >= pheader->size * 0.5) {
    8833            0 :       for (i = 0; i < pheader->max_client_index; i++) {
    8834            0 :          const BUFFER_CLIENT *pc = pheader->client + i;
    8835            0 :          if (pc->pid && pc->write_wait) {
    8836            0 :             BOOL send_wakeup = (pc->write_wait < free_space);
    8837              :             //printf("bm_wakeup_producers: buffer [%s] client [%s] write_wait %d, free_space %d, sending wakeup message %d\n", pheader->name, pc->name, pc->write_wait, free_space, send_wakeup);
    8838            0 :             if (send_wakeup) {
    8839            0 :                ss_resume(pc->port, "B  ");
    8840              :             }
    8841              :          }
    8842              :       }
    8843              :    }
    8844              : }
    8845              : 
    8846            0 : static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
    8847              : {
    8848            0 :    _request_list_mutex.lock();
    8849            0 :    bool locked = true;
    8850            0 :    size_t n = _request_list.size();
    8851              :    /* call dispatcher */
    8852            0 :    for (size_t i = 0; i < n; i++) {
    8853            0 :       if (!locked) {
    8854            0 :          _request_list_mutex.lock();
    8855            0 :          locked = true;
    8856              :       }
    8857            0 :       EventRequest r = _request_list[i];
    8858            0 :       if (r.buffer_handle != buffer_handle)
    8859            0 :          continue;
    8860            0 :       if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
    8861            0 :          continue;
    8862              :       /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
    8863            0 :       _request_list_mutex.unlock();
    8864            0 :       locked = false;
    8865              :       /* if event is fragmented, call defragmenter */
    8866            0 :       if (((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) || ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG))) {
    8867            0 :          bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
    8868              :       } else {
    8869            0 :          r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
    8870              :       }
    8871              :    }
    8872            0 :    if (locked)
    8873            0 :       _request_list_mutex.unlock();
    8874            0 : }
    8875              : 
    8876              : #ifdef LOCAL_ROUTINES
    8877              : 
    8878            0 : static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
    8879              :    /* increment read cache read pointer */
    8880            0 :    pbuf->read_cache_rp += total_size;
    8881              : 
    8882            0 :    if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
    8883            0 :       pbuf->read_cache_rp = 0;
    8884            0 :       pbuf->read_cache_wp = 0;
    8885              :    }
    8886            0 : }
    8887              : 
    8888            0 : static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
    8889              : {
    8890            0 :    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
    8891            0 :       return FALSE;
    8892              : 
    8893            0 :    EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
    8894            0 :    int event_size = pevent->data_size + sizeof(EVENT_HEADER);
    8895            0 :    int total_size = ALIGN8(event_size);
    8896              : 
    8897            0 :    if (ppevent)
    8898            0 :       *ppevent = pevent;
    8899            0 :    if (pevent_size)
    8900            0 :       *pevent_size = event_size;
    8901            0 :    if (ptotal_size)
    8902            0 :       *ptotal_size = total_size;
    8903              : 
    8904            0 :    return TRUE;
    8905              : }
    8906              : 
    8907              : //
    8908              : // return values:
    8909              : // BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
    8910              : // BM_ASYNC_RETURN - buffer is empty
    8911              : // BM_CORRUPTED - buffer is corrupted
    8912              : //
    8913              : 
    8914            0 : static int bm_peek_buffer_locked(BUFFER *pbuf, BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
    8915              : {
    8916            0 :    if (pc->read_pointer == pheader->write_pointer) {
    8917              :       /* no more events buffered for this client */
    8918            0 :       if (!pc->read_wait) {
    8919              :          //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
    8920            0 :          pc->read_wait = TRUE;
    8921              :       }
    8922            0 :       return BM_ASYNC_RETURN;
    8923              :    }
    8924              : 
    8925            0 :    if (pc->read_wait) {
    8926              :       //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
    8927            0 :       pc->read_wait = FALSE;
    8928              :    }
    8929              : 
    8930            0 :    if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
    8931            0 :       cm_msg(MERROR, "bm_peek_buffer_locked", "event buffer \"%s\" is corrupted: client \"%s\" read pointer %d is invalid. buffer read pointer %d, write pointer %d, size %d", pheader->name, pc->name, pc->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
    8932            0 :       return BM_CORRUPTED;
    8933              :    }
    8934              : 
    8935            0 :    char *pdata = (char *) (pheader + 1);
    8936              : 
    8937            0 :    EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
    8938            0 :    int event_size = pevent->data_size + sizeof(EVENT_HEADER);
    8939            0 :    int total_size = ALIGN8(event_size);
    8940              : 
    8941            0 :    if ((total_size <= 0) || (total_size > pheader->size)) {
    8942            0 :       cm_msg(MERROR, "bm_peek_buffer_locked", "event buffer \"%s\" is corrupted: client \"%s\" read pointer %d points to invalid event: data_size %d, event_size %d, total_size %d. buffer size: %d, read_pointer: %d, write_pointer: %d", pheader->name, pc->name, pc->read_pointer, pevent->data_size, event_size, total_size, pheader->size, pheader->read_pointer, pheader->write_pointer);
    8943            0 :       return BM_CORRUPTED;
    8944              :    }
    8945              : 
    8946            0 :    assert(total_size > 0);
    8947            0 :    assert(total_size <= pheader->size);
    8948              : 
    8949            0 :    if (ppevent)
    8950            0 :       *ppevent = pevent;
    8951            0 :    if (pevent_size)
    8952            0 :       *pevent_size = event_size;
    8953            0 :    if (ptotal_size)
    8954            0 :       *ptotal_size = total_size;
    8955              : 
    8956            0 :    return BM_SUCCESS;
    8957              : }
    8958              : 
    8959            0 : static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
    8960              : {
    8961            0 :    const char *pdata = (const char *) (pheader + 1);
    8962              : 
    8963            0 :    if (rp + event_size <= pheader->size) {
    8964              :       /* copy event to cache */
    8965            0 :       memcpy(buf, pdata + rp, event_size);
    8966              :    } else {
    8967              :       /* event is splitted */
    8968            0 :       int size = pheader->size - rp;
    8969            0 :       memcpy(buf, pdata + rp, size);
    8970            0 :       memcpy(buf + size, pdata, event_size - size);
    8971              :    }
    8972            0 : }
    8973              : 
    8974            0 : static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
    8975              : {
    8976            0 :    const char *pdata = (const char *) (pheader + 1);
    8977              : 
    8978            0 :    if (rp + event_size <= pheader->size) {
    8979              :       /* copy event to cache */
    8980            0 :       vecptr->assign(pdata + rp, pdata + rp + event_size);
    8981              :    } else {
    8982              :       /* event is splitted */
    8983            0 :       int size = pheader->size - rp;
    8984            0 :       vecptr->assign(pdata + rp, pdata + rp + size);
    8985            0 :       vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
    8986              :    }
    8987            0 : }
    8988              : 
    8989            0 : static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
    8990              : 
    8991            0 :    BOOL is_requested = FALSE;
    8992              :    int i;
    8993            0 :    for (i = 0; i < pc->max_request_index; i++) {
    8994            0 :       const EVENT_REQUEST *prequest = pc->event_request + i;
    8995            0 :       if (prequest->valid) {
    8996            0 :          if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
    8997              :             /* check if this is a recent event */
    8998            0 :             if (prequest->sampling_type == GET_RECENT) {
    8999            0 :                if (ss_time() - pevent->time_stamp > 1) {
    9000              :                   /* skip that event */
    9001            0 :                   continue;
    9002              :                }
    9003              :             }
    9004              : 
    9005            0 :             is_requested = TRUE;
    9006            0 :             break;
    9007              :          }
    9008              :       }
    9009              :    }
    9010            0 :    return is_requested;
    9011              : }
    9012              : 
    9013              : static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache);
    9014              : 
    9015            0 : static int bm_fill_read_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
    9016              : {
    9017            0 :    BUFFER* pbuf = pbuf_guard.get_pbuf();
    9018            0 :    BUFFER_HEADER* pheader = pbuf->buffer_header;
    9019            0 :    BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
    9020            0 :    BOOL need_wakeup = FALSE;
    9021              : 
    9022              :    //printf("bm_fill_read_cache: [%s] timeout %d, size %d, rp %d, wp %d\n", pheader->name, timeout_msec, pbuf->read_cache_size, pbuf->read_cache_rp, pbuf->read_cache_wp);
    9023              : 
    9024              :    /* loop over all events in the buffer */
    9025              : 
    9026              :    while (1) {
    9027            0 :       EVENT_HEADER *pevent = NULL;
    9028            0 :       int event_size = 3; // poison value
    9029            0 :       int total_size = 3; // poison value
    9030              : 
    9031            0 :       int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
    9032            0 :       if (status == BM_CORRUPTED) {
    9033            0 :          return status;
    9034            0 :       } else if (status != BM_SUCCESS) {
    9035              :          /* event buffer is empty */
    9036            0 :          if (timeout_msec == BM_NO_WAIT) {
    9037            0 :             if (need_wakeup)
    9038            0 :                bm_wakeup_producers_locked(pheader, pc);
    9039            0 :             if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
    9040              :                // read cache is empty
    9041            0 :                return BM_ASYNC_RETURN;
    9042              :             }
    9043            0 :             return BM_SUCCESS;
    9044              :          }
    9045              : 
    9046            0 :          int status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, TRUE);
    9047              : 
    9048            0 :          if (status != BM_SUCCESS) {
    9049              :             // we only come here with SS_ABORT & co
    9050            0 :             return status;
    9051              :          }
    9052              : 
    9053              :          // make sure we wait for new event only once
    9054            0 :          timeout_msec = BM_NO_WAIT;
    9055              :          // go back to bm_peek_buffer_locked
    9056            0 :          continue;
    9057            0 :       }
    9058              : 
    9059              :       /* loop over all requests: if this event matches a request,
    9060              :        * copy it to the read cache */
    9061              : 
    9062            0 :       BOOL is_requested = bm_check_requests(pc, pevent);
    9063              : 
    9064            0 :       if (is_requested) {
    9065            0 :          if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
    9066              :             /* read cache is full */
    9067            0 :             if (need_wakeup)
    9068            0 :                bm_wakeup_producers_locked(pheader, pc);
    9069            0 :             return BM_SUCCESS;
    9070              :          }
    9071              : 
    9072            0 :          bm_read_from_buffer_locked(pheader, pc->read_pointer, pbuf->read_cache + pbuf->read_cache_wp, event_size);
    9073              : 
    9074            0 :          pbuf->read_cache_wp += total_size;
    9075              : 
    9076              :          /* update statistics */
    9077            0 :          pheader->num_out_events++;
    9078            0 :          pbuf->count_read++;
    9079            0 :          pbuf->bytes_read += event_size;
    9080              :       }
    9081              : 
    9082              :       /* shift read pointer */
    9083              : 
    9084            0 :       int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
    9085            0 :       pc->read_pointer = new_read_pointer;
    9086              : 
    9087            0 :       need_wakeup = TRUE;
    9088            0 :    }
    9089              :    /* NOT REACHED */
    9090              : }
    9091              : 
    9092            0 : static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
    9093              :    /* now convert event header */
    9094            0 :    if (convert_flags) {
    9095            0 :       rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
    9096            0 :       rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
    9097            0 :       rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
    9098            0 :       rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
    9099            0 :       rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
    9100              :    }
    9101            0 : }
    9102              : 
    9103           11 : static int bm_wait_for_free_space_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec, int requested_space, bool unlock_write_cache)
    9104              : {
    9105              :    // return values:
    9106              :    // BM_SUCCESS - have "requested_space" bytes free in the buffer
    9107              :    // BM_CORRUPTED - shared memory is corrupted
    9108              :    // BM_NO_MEMORY - asked for more than buffer size
    9109              :    // BM_ASYNC_RETURN - timeout waiting for free space
    9110              :    // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
    9111              :    // SS_ABORT - we are told to shutdown (locks releases)
    9112              : 
    9113              :    int status;
    9114           11 :    BUFFER* pbuf = pbuf_guard.get_pbuf();
    9115           11 :    BUFFER_HEADER *pheader = pbuf->buffer_header;
    9116           11 :    char *pdata = (char *) (pheader + 1);
    9117              : 
    9118              :    /* make sure the buffer never completely full:
    9119              :     * read pointer and write pointer would coincide
    9120              :     * and the code cannot tell if it means the
    9121              :     * buffer is 100% full or 100% empty. It will explode
    9122              :     * or lose events */
    9123           11 :    requested_space += 100;
    9124              : 
    9125           11 :    if (requested_space >= pheader->size)
    9126            0 :       return BM_NO_MEMORY;
    9127              : 
    9128           11 :    DWORD time_start = ss_millitime();
    9129           11 :    DWORD time_end = time_start + timeout_msec;
    9130              : 
    9131              :    //DWORD blocking_time = 0;
    9132              :    //int blocking_loops = 0;
    9133           11 :    int blocking_client_index = -1;
    9134              :    char blocking_client_name[NAME_LENGTH];
    9135           11 :    blocking_client_name[0] = 0;
    9136              : 
    9137              :    while (1) {
    9138              :       while (1) {
    9139              :          /* check if enough space in buffer */
    9140              : 
    9141           11 :          int free = pheader->read_pointer - pheader->write_pointer;
    9142           11 :          if (free <= 0)
    9143           11 :             free += pheader->size;
    9144              : 
    9145              :          //printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, timeout %d\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, timeout_msec);
    9146              : 
    9147           11 :          if (requested_space < free) { /* note the '<' to avoid 100% filling */
    9148              :             //if (blocking_loops) {
    9149              :             //   DWORD wait_time = ss_millitime() - blocking_time;
    9150              :             //   printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
    9151              :             //}
    9152              : 
    9153           11 :             if (pbuf->wait_start_time != 0) {
    9154            0 :                DWORD now = ss_millitime();
    9155            0 :                DWORD wait_time = now - pbuf->wait_start_time;
    9156            0 :                pbuf->time_write_wait += wait_time;
    9157            0 :                pbuf->wait_start_time = 0;
    9158            0 :                int iclient = pbuf->wait_client_index;
    9159              :                //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
    9160            0 :                if (iclient >= 0 && iclient < MAX_CLIENTS) {
    9161            0 :                   pbuf->client_count_write_wait[iclient] += 1;
    9162            0 :                   pbuf->client_time_write_wait[iclient] += wait_time;
    9163              :                }
    9164              :             }
    9165              : 
    9166              :             //if (blocking_loops > 0) {
    9167              :             //   printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, timeout %d, found space after %d waits\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, timeout_msec, blocking_loops);
    9168              :             //}
    9169              : 
    9170           11 :             return BM_SUCCESS;
    9171              :          }
    9172              : 
    9173            0 :          if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
    9174            0 :             cm_msg(MERROR, "bm_wait_for_free_space",
    9175              :                    "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
    9176            0 :                    pheader->name,
    9177              :                    pheader->read_pointer,
    9178              :                    pheader->write_pointer,
    9179              :                    pheader->size,
    9180              :                    free,
    9181              :                    requested_space);
    9182            0 :             return BM_CORRUPTED;
    9183              :          }
    9184              : 
    9185            0 :          const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
    9186            0 :          int event_size = pevent->data_size + sizeof(EVENT_HEADER);
    9187            0 :          int total_size = ALIGN8(event_size);
    9188              : 
    9189              : #if 0
    9190              :          printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, blocking event size %d/%d\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, event_size, total_size);
    9191              : #endif
    9192              : 
    9193            0 :          if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
    9194            0 :             cm_msg(MERROR, "bm_wait_for_free_space",
    9195              :                    "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer points to an invalid event: data_size %d, event size %d, total_size %d",
    9196            0 :                    pheader->name,
    9197              :                    pheader->read_pointer,
    9198              :                    pheader->write_pointer,
    9199              :                    pheader->size,
    9200              :                    free,
    9201              :                    requested_space,
    9202            0 :                    pevent->data_size,
    9203              :                    event_size,
    9204              :                    total_size);
    9205            0 :             return BM_CORRUPTED;
    9206              :          }
    9207              : 
    9208            0 :          int blocking_client = -1;
    9209              : 
    9210              :          int i;
    9211            0 :          for (i = 0; i < pheader->max_client_index; i++) {
    9212            0 :             BUFFER_CLIENT *pc = pheader->client + i;
    9213            0 :             if (pc->pid) {
    9214            0 :                if (pc->read_pointer == pheader->read_pointer) {
    9215              :                   /*
    9216              :                     First assume that the client with the "minimum" read pointer
    9217              :                     is not really blocking due to a GET_ALL request.
    9218              :                   */
    9219            0 :                   BOOL blocking = FALSE;
    9220              :                   //int blocking_request_id = -1;
    9221              : 
    9222              :                   int j;
    9223            0 :                   for (j = 0; j < pc->max_request_index; j++) {
    9224            0 :                      const EVENT_REQUEST *prequest = pc->event_request + j;
    9225            0 :                      if (prequest->valid
    9226            0 :                          && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
    9227            0 :                         if (prequest->sampling_type & GET_ALL) {
    9228            0 :                            blocking = TRUE;
    9229              :                            //blocking_request_id = prequest->id;
    9230            0 :                            break;
    9231              :                         }
    9232              :                      }
    9233              :                   }
    9234              : 
    9235              :                   //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
    9236              : 
    9237            0 :                   if (blocking) {
    9238            0 :                      blocking_client = i;
    9239            0 :                      break;
    9240              :                   }
    9241              : 
    9242            0 :                   pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
    9243              :                }
    9244              :             }
    9245              :          } /* client loop */
    9246              : 
    9247            0 :          if (blocking_client >= 0) {
    9248            0 :             blocking_client_index = blocking_client;
    9249            0 :             mstrlcpy(blocking_client_name, pheader->client[blocking_client].name, sizeof(blocking_client_name));
    9250              :             //if (!blocking_time) {
    9251              :             //   blocking_time = ss_millitime();
    9252              :             //}
    9253              : 
    9254              :             //printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, timeout %d, must wait for more space!\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, timeout_msec);
    9255              : 
    9256              :             // from this "break" we go into timeout check and sleep/wait.
    9257            0 :             break;
    9258              :          }
    9259              : 
    9260              :          /* no blocking clients. move the read pointer and again check for free space */
    9261              : 
    9262            0 :          BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
    9263              : 
    9264            0 :          if (!moved) {
    9265            0 :             cm_msg(MERROR, "bm_wait_for_free_space",
    9266              :                    "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer did not move as expected",
    9267            0 :                    pheader->name,
    9268              :                    pheader->read_pointer,
    9269              :                    pheader->write_pointer,
    9270              :                    pheader->size,
    9271              :                    free,
    9272              :                    requested_space);
    9273            0 :             return BM_CORRUPTED;
    9274              :          }
    9275              : 
    9276              :          /* we freed one event, loop back to the check for free space */
    9277            0 :       }
    9278              : 
    9279              :       //blocking_loops++;
    9280              : 
    9281              :       /* at least one client is blocking */
    9282              : 
    9283            0 :       BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
    9284            0 :       pc->write_wait = requested_space;
    9285              : 
    9286            0 :       if (pbuf->wait_start_time == 0) {
    9287            0 :          pbuf->wait_start_time = ss_millitime();
    9288            0 :          pbuf->count_write_wait++;
    9289            0 :          if (requested_space > pbuf->max_requested_space)
    9290            0 :             pbuf->max_requested_space = requested_space;
    9291            0 :          pbuf->wait_client_index = blocking_client_index;
    9292              :       }
    9293              : 
    9294            0 :       DWORD now = ss_millitime();
    9295              : 
    9296              :       //printf("bm_wait_for_free_space: start 0x%08x, now 0x%08x, end 0x%08x, timeout %d, wait %d\n", time_start, now, time_end, timeout_msec, time_end - now);
    9297              : 
    9298            0 :       int sleep_time_msec = 1000;
    9299              : 
    9300            0 :       if (timeout_msec == BM_WAIT) {
    9301              :          // wait forever
    9302            0 :       } else if (timeout_msec == BM_NO_WAIT) {
    9303              :          // no wait
    9304            0 :          return BM_ASYNC_RETURN;
    9305              :       } else {
    9306              :          // check timeout
    9307            0 :          if (now >= time_end) {
    9308              :             // timeout!
    9309            0 :             return BM_ASYNC_RETURN;
    9310              :          }
    9311              : 
    9312            0 :          sleep_time_msec = time_end - now;
    9313              : 
    9314            0 :          if (sleep_time_msec <= 0) {
    9315            0 :             sleep_time_msec = 10;
    9316            0 :          } else if (sleep_time_msec > 1000) {
    9317            0 :             sleep_time_msec = 1000;
    9318              :          }
    9319              :       }
    9320              : 
    9321            0 :       ss_suspend_get_buffer_port(ss_gettid(), &pc->port);
    9322              : 
    9323              :       /* before waiting, unlock everything in the correct order */
    9324              : 
    9325            0 :       pbuf_guard.unlock();
    9326              : 
    9327            0 :       if (unlock_write_cache)
    9328            0 :          pbuf->write_cache_mutex.unlock();
    9329              : 
    9330              :       //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
    9331              : 
    9332              : #ifdef DEBUG_MSG
    9333              :       cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
    9334              : #endif
    9335              : 
    9336              :       ///* signal other clients wait mode */
    9337              :       //int idx = bm_validate_client_index_locked(pbuf, FALSE);
    9338              :       //if (idx >= 0)
    9339              :       //   pheader->client[idx].write_wait = requested_space;
    9340              : 
    9341              :       //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
    9342              : 
    9343            0 :       status = ss_suspend(sleep_time_msec, MSG_BM);
    9344              : 
    9345              :       /* we are told to shutdown */
    9346            0 :       if (status == SS_ABORT) {
    9347              :          // NB: buffer is locked!
    9348            0 :          return SS_ABORT;
    9349              :       }
    9350              : 
    9351              :       /* make sure we do sleep in this loop:
    9352              :        * if we are the mserver receiving data on the event
    9353              :        * socket and the data buffer is full, ss_suspend() will
    9354              :        * never sleep: it will detect data on the event channel,
    9355              :        * call rpc_server_receive() (recursively, we already *are* in
    9356              :        * rpc_server_receive()) and return without sleeping. Result
    9357              :        * is a busy loop waiting for free space in data buffer */
    9358              : 
    9359              :       /* update May 2021: ss_suspend(MSG_BM) no longer looks at
    9360              :        * the event socket, and should sleep now, so this sleep below
    9361              :        * maybe is not needed now. but for safety, I keep it. K.O. */
    9362              : 
    9363            0 :       if (status != SS_TIMEOUT) {
    9364              :          //printf("ss_suspend: status %d\n", status);
    9365            0 :          ss_sleep(1);
    9366              :       }
    9367              : 
    9368              :       /* we may be stuck in this loop for an arbitrary long time,
    9369              :        * depending on how other buffer clients read the accumulated data
    9370              :        * so we should update all the timeouts & etc. K.O. */
    9371              : 
    9372            0 :       cm_periodic_tasks();
    9373              : 
    9374              :       /* lock things again in the correct order */
    9375              : 
    9376            0 :       if (unlock_write_cache) {
    9377            0 :          status = bm_lock_buffer_write_cache(pbuf);
    9378              : 
    9379            0 :          if (status != BM_SUCCESS) {
    9380              :             // bail out with all locks released
    9381            0 :             return status;
    9382              :          }
    9383              :       }
    9384              : 
    9385            0 :       if (!pbuf_guard.relock()) {
    9386            0 :          if (unlock_write_cache) {
    9387            0 :             pbuf->write_cache_mutex.unlock();
    9388              :          }
    9389              : 
    9390              :          // bail out with all locks released
    9391            0 :          return pbuf_guard.get_status();
    9392              :       }
    9393              : 
    9394              :       /* revalidate the client index: we could have been removed from the buffer while sleeping */
    9395            0 :       pc = bm_get_my_client_locked(pbuf_guard);
    9396              : 
    9397            0 :       pc->write_wait = 0;
    9398              : 
    9399              :       ///* validate client index: we could have been removed from the buffer */
    9400              :       //idx = bm_validate_client_index_locked(pbuf, FALSE);
    9401              :       //if (idx >= 0)
    9402              :       //   pheader->client[idx].write_wait = 0;
    9403              :       //else {
    9404              :       //   cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
    9405              :       //   status = SS_ABORT;
    9406              :       //}
    9407              : 
    9408              : #ifdef DEBUG_MSG
    9409              :       cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
    9410              : #endif
    9411              : 
    9412            0 :    }
    9413              : }
    9414              : 
    9415            0 : static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache)
    9416              : {
    9417            0 :    BUFFER* pbuf = pbuf_guard.get_pbuf();
    9418            0 :    BUFFER_HEADER* pheader = pbuf->buffer_header;
    9419              :    
    9420              :    //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
    9421              :    
    9422            0 :    if (pc->read_pointer != pheader->write_pointer) {
    9423              :       // buffer has data
    9424            0 :       return BM_SUCCESS;
    9425              :    }
    9426              : 
    9427            0 :    if (timeout_msec == BM_NO_WAIT) {
    9428              :       /* event buffer is empty and we are told to not wait */
    9429            0 :       if (!pc->read_wait) {
    9430              :          //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
    9431            0 :          pc->read_wait = TRUE;
    9432              :       }
    9433            0 :       return BM_ASYNC_RETURN;
    9434              :    }
    9435              : 
    9436            0 :    DWORD time_start = ss_millitime();
    9437            0 :    DWORD time_wait  = time_start + timeout_msec;
    9438            0 :    DWORD sleep_time = 1000;
    9439            0 :    if (timeout_msec == BM_NO_WAIT) {
    9440              :       // default sleep time
    9441            0 :    } else if (timeout_msec == BM_WAIT) {
    9442              :       // default sleep time
    9443              :    } else {
    9444            0 :       if (sleep_time > (DWORD)timeout_msec)
    9445            0 :          sleep_time = timeout_msec;
    9446              :    }
    9447              : 
    9448              :    //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
    9449              : 
    9450            0 :    while (pc->read_pointer == pheader->write_pointer) {
    9451              :       /* wait until there is data in the buffer (write pointer moves) */
    9452              : 
    9453            0 :       if (!pc->read_wait) {
    9454              :          //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
    9455            0 :          pc->read_wait = TRUE;
    9456              :       }
    9457              : 
    9458            0 :       pc->last_activity = ss_millitime();
    9459              : 
    9460            0 :       ss_suspend_get_buffer_port(ss_gettid(), &pc->port);
    9461              : 
    9462              :       // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
    9463              : 
    9464            0 :       pbuf_guard.unlock();
    9465              : 
    9466            0 :       if (unlock_read_cache)
    9467            0 :          pbuf->read_cache_mutex.unlock();
    9468              : 
    9469            0 :       int status = ss_suspend(sleep_time, MSG_BM);
    9470              : 
    9471            0 :       if (timeout_msec == BM_NO_WAIT) {
    9472              :          // return immediately
    9473            0 :       } else if (timeout_msec == BM_WAIT) {
    9474              :          // wait forever
    9475              :       } else {
    9476            0 :          DWORD now = ss_millitime();
    9477              :          //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
    9478            0 :          if (now >= time_wait) {
    9479            0 :             timeout_msec = BM_NO_WAIT; // cause immediate return
    9480              :          } else {
    9481            0 :             sleep_time = time_wait - now;
    9482            0 :             if (sleep_time > 1000)
    9483            0 :                sleep_time = 1000;
    9484              :             //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
    9485              :          }
    9486              :       }
    9487              : 
    9488              :       // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
    9489              : 
    9490            0 :       if (unlock_read_cache) {
    9491            0 :          status = bm_lock_buffer_read_cache(pbuf);
    9492            0 :          if (status != BM_SUCCESS) {
    9493              :             // bail out with all locks released
    9494            0 :             return status;
    9495              :          }
    9496              :       }
    9497              : 
    9498            0 :       if (!pbuf_guard.relock()) {
    9499            0 :          if (unlock_read_cache) {
    9500            0 :             pbuf->read_cache_mutex.unlock();
    9501              :          }
    9502              :          // bail out with all locks released
    9503            0 :          return pbuf_guard.get_status();
    9504              :       }
    9505              :       
    9506              :       /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
    9507              :        * because we may have been removed from the buffer by bm_cleanup() & co
    9508              :        * due to a timeout or whatever. */
    9509            0 :       pc = bm_get_my_client_locked(pbuf_guard);
    9510              : 
    9511              :       /* return if TCP connection broken */
    9512            0 :       if (status == SS_ABORT)
    9513            0 :          return SS_ABORT;
    9514              : 
    9515            0 :       if (timeout_msec == BM_NO_WAIT)
    9516            0 :          return BM_ASYNC_RETURN;
    9517              :    }
    9518              : 
    9519            0 :    if (pc->read_wait) {
    9520              :       //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
    9521            0 :       pc->read_wait = FALSE;
    9522              :    }
    9523              : 
    9524            0 :    return BM_SUCCESS;
    9525              : }
    9526              : 
    9527           11 : static void bm_write_to_buffer_locked(BUFFER_HEADER *pheader, int sg_n, const char* const sg_ptr[], const size_t sg_len[], size_t total_size)
    9528              : {
    9529           11 :    char *pdata = (char *) (pheader + 1);
    9530              : 
    9531              :    //int old_write_pointer = pheader->write_pointer;
    9532              : 
    9533              :    /* new event fits into the remaining space? */
    9534           11 :    if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
    9535              :       //memcpy(pdata + pheader->write_pointer, pevent, event_size);
    9536           11 :       char* wptr = pdata + pheader->write_pointer;
    9537           22 :       for (int i=0; i<sg_n; i++) {
    9538              :          //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
    9539           11 :          memcpy(wptr, sg_ptr[i], sg_len[i]);
    9540           11 :          wptr += sg_len[i];
    9541              :       }
    9542           11 :       pheader->write_pointer = pheader->write_pointer + total_size;
    9543           11 :       assert(pheader->write_pointer <= pheader->size);
    9544              :       /* remaining space is smaller than size of an event header? */
    9545           11 :       if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
    9546              :          // note: ">" here to match "bm_incr_rp". If remaining space is exactly
    9547              :          // equal to the event header size, we will write the next event header here,
    9548              :          // then wrap the pointer and write the event data at the beginning of the buffer.
    9549              :          //printf("bm_write_to_buffer_locked: truncate wp %d. buffer size %d, remaining %d, event header size %d, event size %d, total size %d\n", pheader->write_pointer, pheader->size, pheader->size-pheader->write_pointer, (int)sizeof(EVENT_HEADER), event_size, total_size);
    9550            0 :          pheader->write_pointer = 0;
    9551              :       }
    9552              :    } else {
    9553              :       /* split event */
    9554            0 :       size_t size = pheader->size - pheader->write_pointer;
    9555              : 
    9556              :       //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
    9557              : 
    9558              :       //memcpy(pdata + pheader->write_pointer, pevent, size);
    9559              :       //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
    9560              : 
    9561            0 :       char* wptr = pdata + pheader->write_pointer;
    9562            0 :       size_t count = 0;
    9563              : 
    9564              :       // copy first part
    9565              : 
    9566            0 :       int i = 0;
    9567            0 :       for (; i<sg_n; i++) {
    9568            0 :          if (count + sg_len[i] > size)
    9569            0 :             break;
    9570            0 :          memcpy(wptr, sg_ptr[i], sg_len[i]);
    9571            0 :          wptr  += sg_len[i];
    9572            0 :          count += sg_len[i];
    9573              :       }
    9574              : 
    9575              :       //printf("wptr %d, count %d\n", wptr-pdata, count);
    9576              : 
    9577              :       // split segment
    9578              : 
    9579            0 :       size_t first = size - count;
    9580            0 :       size_t second = sg_len[i] - first;
    9581            0 :       assert(first + second == sg_len[i]);
    9582            0 :       assert(count + first == size);
    9583              :       
    9584              :       //printf("first %d, second %d\n", first, second);
    9585              :       
    9586            0 :       memcpy(wptr, sg_ptr[i], first);
    9587            0 :       wptr = pdata + 0;
    9588            0 :       count += first;
    9589            0 :       memcpy(wptr, sg_ptr[i] + first, second);
    9590            0 :       wptr  += second;
    9591            0 :       count += second;
    9592            0 :       i++;
    9593              : 
    9594              :       // copy remaining
    9595              : 
    9596            0 :       for (; i<sg_n; i++) {
    9597            0 :          memcpy(wptr, sg_ptr[i], sg_len[i]);
    9598            0 :          wptr  += sg_len[i];
    9599            0 :          count += sg_len[i];
    9600              :       }
    9601              : 
    9602              :       //printf("wptr %d, count %d\n", wptr-pdata, count);
    9603              : 
    9604              :       //printf("bm_write_to_buffer_locked: wrap wp %d -> %d. buffer size %d, available %d, wrote %d, remaining %d, event size %d, total size %d\n", pheader->write_pointer, total_size-size, pheader->size, pheader->size-pheader->write_pointer, size, pheader->size - (pheader->write_pointer+size), event_size, total_size);
    9605              : 
    9606            0 :       pheader->write_pointer = total_size - size;
    9607              :    }
    9608              : 
    9609              :    //printf("bm_write_to_buffer_locked: buf [%s] size %d, wrote %d/%d, wp %d -> %d\n", pheader->name, pheader->size, event_size, total_size, old_write_pointer, pheader->write_pointer);
    9610           11 : }
    9611              : 
    9612           11 : static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
    9613           11 :    if (pc->pid) {
    9614              :       int j;
    9615           11 :       for (j = 0; j < pc->max_request_index; j++) {
    9616            0 :          const EVENT_REQUEST *prequest = pc->event_request + j;
    9617            0 :          if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
    9618            0 :             return prequest->id;
    9619              :          }
    9620              :       }
    9621              :    }
    9622              : 
    9623           11 :    return -1;
    9624              : }
    9625              : 
    9626           11 : static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
    9627           11 :    if (request_id >= 0) {
    9628              :       /* if that client has a request and is suspended, wake it up */
    9629            0 :       if (pc->read_wait) {
    9630              :          char str[80];
    9631            0 :          sprintf(str, "B %s %d", pheader->name, request_id);
    9632            0 :          ss_resume(pc->port, str);
    9633              :          //printf("bm_notify_reader_locked: buffer [%s] client [%s] request_id %d, port %d, message [%s]\n", pheader->name, pc->name, request_id, pc->port, str);
    9634              :          //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
    9635            0 :          pc->read_wait = FALSE;
    9636              :       }
    9637              :    }
    9638           11 : }
    9639              : 
    9640              : #endif // LOCAL_ROUTINES
    9641              : 
    9642              : #if 0
    9643              : INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
    9644              : {
    9645              :    //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
    9646              : 
    9647              :    DWORD time_start = ss_millitime();
    9648              :    DWORD time_end = time_start + timeout_msec;
    9649              :    
    9650              :    int xtimeout_msec = timeout_msec;
    9651              : 
    9652              :    while (1) {
    9653              :       if (timeout_msec == BM_WAIT) {
    9654              :          xtimeout_msec = 1000;
    9655              :       } else if (timeout_msec == BM_NO_WAIT) {
    9656              :          xtimeout_msec = BM_NO_WAIT;
    9657              :       } else {
    9658              :          if (xtimeout_msec > 1000) {
    9659              :             xtimeout_msec = 1000;
    9660              :          }
    9661              :       }
    9662              :    
    9663              :       int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
    9664              : 
    9665              :       //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
    9666              : 
    9667              :       if (status == BM_ASYNC_RETURN) {
    9668              :          if (timeout_msec == BM_WAIT) {
    9669              :             // BM_WAIT means wait forever
    9670              :             continue;
    9671              :          } else if (timeout_msec == BM_NO_WAIT) {
    9672              :             // BM_NO_WAIT means do not wait
    9673              :             return status;
    9674              :          } else {
    9675              :             DWORD now = ss_millitime();
    9676              :             if (now >= time_end) {
    9677              :                // timeout, return BM_ASYNC_RETURN
    9678              :                return status;
    9679              :             }
    9680              : 
    9681              :             DWORD remain = time_end - now;
    9682              : 
    9683              :             if (remain < xtimeout_msec) {
    9684              :                xtimeout_msec = remain;
    9685              :             }
    9686              : 
    9687              :             // keep asking for event...
    9688              :             continue;
    9689              :          }
    9690              :       } else if (status == BM_SUCCESS) {
    9691              :          // success, return BM_SUCCESS
    9692              :          return status;
    9693              :       } else {
    9694              :          // error
    9695              :          return status;
    9696              :       }
    9697              :    }
    9698              : }
    9699              : #endif
    9700              : 
    9701           11 : INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
    9702              : {
    9703           11 :    const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
    9704           11 :    const DWORD data_size = pevent->data_size; // 32-bit unsigned value
    9705              : 
    9706           11 :    if (data_size == 0) {
    9707            0 :       cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
    9708            0 :       return BM_INVALID_SIZE;
    9709              :    }
    9710              : 
    9711           11 :    if (data_size > MAX_DATA_SIZE) {
    9712            0 :       cm_msg(MERROR, "bm_send_event", "invalid event data size %d (0x%x) maximum is %d (0x%x)", data_size, data_size, MAX_DATA_SIZE, MAX_DATA_SIZE);
    9713            0 :       return BM_INVALID_SIZE;
    9714              :    }
    9715              : 
    9716           11 :    const size_t event_size = sizeof(EVENT_HEADER) + data_size;
    9717              : 
    9718              :    //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
    9719              : 
    9720           11 :    if (rpc_is_remote()) {
    9721              :       //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
    9722            0 :       return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
    9723              :    } else {
    9724           11 :       return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
    9725              :    }
    9726              : }
    9727              : 
    9728            0 : int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
    9729              : {
    9730            0 :    const char* cptr = event.data();
    9731            0 :    size_t clen = event.size();
    9732            0 :    return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
    9733              : }
    9734              : 
    9735            0 : int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
    9736              : {
    9737            0 :    int sg_n = event.size();
    9738            0 :    const char* sg_ptr[sg_n];
    9739            0 :    size_t sg_len[sg_n];
    9740            0 :    for (int i=0; i<sg_n; i++) {
    9741            0 :       sg_ptr[i] = event[i].data();
    9742            0 :       sg_len[i] = event[i].size();
    9743              :    }
    9744            0 :    return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
    9745            0 : }
    9746              : 
    9747              : #ifdef LOCAL_ROUTINES
    9748              : static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec);
    9749              : #endif
    9750              : 
    9751              : /********************************************************************/
    9752              : /**
    9753              : Sends an event to a buffer.
    9754              : This function check if the buffer has enough space for the
    9755              : event, then copies the event to the buffer in shared memory.
    9756              : If clients have requests for the event, they are notified via an UDP packet.
    9757              : \code
    9758              : char event[1000];
    9759              : // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
    9760              : bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
    9761              : 
    9762              : // set first byte of event
    9763              : *(event+sizeof(EVENT_HEADER)) = <...>
    9764              : #include <stdio.h>
    9765              : #include "midas.h"
    9766              : main()
    9767              : {
    9768              :  INT status, i;
    9769              :  HNDLE hbuf;
    9770              :  char event[1000];
    9771              :  status = cm_connect_experiment("", "Sample", "Producer", NULL);
    9772              :  if (status != CM_SUCCESS)
    9773              :  return 1;
    9774              :  bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
    9775              : 
    9776              :  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
    9777              :  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
    9778              : 
    9779              :  // set event data
    9780              :  for (i=0 ; i<100 ; i++)
    9781              :  *(event+sizeof(EVENT_HEADER)+i) = i;
    9782              :  // send event
    9783              :  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), BM_WAIT);
    9784              :  cm_disconnect_experiment();
    9785              :  return 0;
    9786              : }
    9787              : \endcode
    9788              : @param buffer_handle Buffer handle obtained via bm_open_buffer()
    9789              : @param source Address of event buffer
    9790              : @param buf_size Size of event including event header in bytes
    9791              : @param timeout_msec Timeout waiting for free space in the event buffer. If BM_WAIT, wait forever.
    9792              : If BM_NO_WAIT, the function returns immediately with a
    9793              : value of BM_ASYNC_RETURN without writing the event to the buffer
    9794              : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
    9795              : BM_ASYNC_RETURN Routine called with timeout_msec == BM_NO_WAIT and
    9796              : buffer has not enough space to receive event<br>
    9797              : BM_NO_MEMORY   Event is too large for network buffer or event buffer.
    9798              : One has to increase the event buffer size "/Experiment/Buffer sizes/SYSTEM"
    9799              : and/or /Experiment/MAX_EVENT_SIZE in ODB.
    9800              : */
    9801           11 : int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
    9802              : {
    9803           11 :    if (rpc_is_remote())
    9804            0 :       return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
    9805              : 
    9806           11 :    if (sg_n < 1) {
    9807            0 :       cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
    9808            0 :       return BM_INVALID_SIZE;
    9809              :    }
    9810              : 
    9811           11 :    if (sg_ptr[0] == NULL) {
    9812            0 :       cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
    9813            0 :       return BM_INVALID_SIZE;
    9814              :    }
    9815              : 
    9816           11 :    if (sg_len[0] < sizeof(EVENT_HEADER)) {
    9817            0 :       cm_msg(MERROR, "bm_send_event", "invalid sg_len[0] value %d is smaller than event header size %d", (int)sg_len[0], (int)sizeof(EVENT_HEADER));
    9818            0 :       return BM_INVALID_SIZE;
    9819              :    }
    9820              : 
    9821           11 :    const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
    9822              :    
    9823           11 :    const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
    9824           11 :    const DWORD data_size = pevent->data_size; // 32-bit unsigned value
    9825              : 
    9826           11 :    if (data_size == 0) {
    9827            0 :       cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
    9828            0 :       return BM_INVALID_SIZE;
    9829              :    }
    9830              : 
    9831           11 :    if (data_size > MAX_DATA_SIZE) {
    9832            0 :       cm_msg(MERROR, "bm_send_event", "invalid event data size %d (0x%x) maximum is %d (0x%x)", data_size, data_size, MAX_DATA_SIZE, MAX_DATA_SIZE);
    9833            0 :       return BM_INVALID_SIZE;
    9834              :    }
    9835              : 
    9836           11 :    const size_t event_size = sizeof(EVENT_HEADER) + data_size;
    9837              : 
    9838           11 :    size_t count = 0;
    9839           22 :    for (int i=0; i<sg_n; i++) {
    9840           11 :       count += sg_len[i];
    9841              :    }
    9842              : 
    9843           11 :    if (count != event_size) {
    9844            0 :       cm_msg(MERROR, "bm_send_event", "data size mismatch: event data_size %d, event_size %d not same as sum of sg_len %d", (int)data_size, (int)event_size, (int)count);
    9845            0 :       return BM_INVALID_SIZE;
    9846              :    }
    9847              : 
    9848              :    //printf("bm_send_event_sg: pevent %p, event_id 0x%04x, serial 0x%08x, data_size %d, event_size %d, total_size %d\n", pevent, pevent->event_id, pevent->serial_number, (int)pevent->data_size, (int)event_size, (int)total_size);
    9849              : 
    9850              : #ifdef LOCAL_ROUTINES
    9851              :    {
    9852           11 :       int status = 0;
    9853           11 :       const size_t total_size = ALIGN8(event_size);
    9854              : 
    9855           11 :       BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
    9856              : 
    9857           11 :       if (!pbuf)
    9858            0 :          return status;
    9859              : 
    9860              :       /* round up total_size to next DWORD boundary */
    9861              :       //int total_size = ALIGN8(event_size);
    9862              : 
    9863              :       /* check if write cache is enabled */
    9864           11 :       if (pbuf->write_cache_size) {
    9865            0 :          status = bm_lock_buffer_write_cache(pbuf);
    9866              : 
    9867            0 :          if (status != BM_SUCCESS)
    9868            0 :             return status;
    9869              :          
    9870              :          /* check if write cache is enabled */
    9871            0 :          if (pbuf->write_cache_size) {
    9872            0 :             size_t max_event_size = pbuf->write_cache_size/MAX_WRITE_CACHE_EVENT_SIZE_DIV;
    9873            0 :             bool too_big = event_size > max_event_size;
    9874              : 
    9875              :             //printf("bm_send_event: write %zu/%zu max %zu, cache size %zu, wp %zu\n", event_size, total_size, max_event_size, pbuf->write_cache_size.load(), pbuf->write_cache_wp);
    9876              : 
    9877              :             /* if this event does not fit into the write cache, flush the write cache */
    9878            0 :             if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
    9879              :                //printf("bm_send_event: write %zu/%zu but cache is full, size %zu, wp %zu\n", event_size, total_size, pbuf->write_cache_size.load(), pbuf->write_cache_wp);
    9880              : 
    9881            0 :                bm_lock_buffer_guard pbuf_guard(pbuf);
    9882              : 
    9883            0 :                if (!pbuf_guard.is_locked()) {
    9884            0 :                   pbuf->write_cache_mutex.unlock();
    9885            0 :                   return pbuf_guard.get_status();
    9886              :                }
    9887              : 
    9888            0 :                int status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
    9889              : 
    9890            0 :                if (pbuf_guard.is_locked()) {
    9891              :                   // check if bm_wait_for_free_space() failed to relock the buffer
    9892            0 :                   pbuf_guard.unlock();
    9893              :                }
    9894              : 
    9895            0 :                if (status != BM_SUCCESS) {
    9896            0 :                   pbuf->write_cache_mutex.unlock();
    9897              :                   // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
    9898            0 :                   if (status == BM_NO_MEMORY)
    9899            0 :                      cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
    9900            0 :                   return status;
    9901              :                }
    9902              : 
    9903              :                // write cache must be empty here
    9904            0 :                assert(pbuf->write_cache_wp == 0);
    9905            0 :             }
    9906              : 
    9907              :             /* write this event into the write cache, if it is not too big and if it fits */
    9908            0 :             if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
    9909              :                //printf("bm_send_event: write %d/%d to cache size %d, wp %d\n", (int)event_size, (int)total_size, (int)pbuf->write_cache_size, (int)pbuf->write_cache_wp);
    9910              :                
    9911            0 :                char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
    9912              :                
    9913            0 :                for (int i=0; i<sg_n; i++) {
    9914            0 :                   memcpy(wptr, sg_ptr[i], sg_len[i]);
    9915            0 :                   wptr += sg_len[i];
    9916              :                }
    9917              : 
    9918            0 :                pbuf->write_cache_wp += total_size;
    9919              : 
    9920            0 :                pbuf->write_cache_mutex.unlock();
    9921            0 :                return BM_SUCCESS;
    9922              :             }
    9923              :          }
    9924              : 
    9925              :          /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
    9926            0 :          pbuf->write_cache_mutex.unlock();
    9927              :       }
    9928              : 
    9929              :       /* we come here only for events that are too big to fit into the cache */
    9930              : 
    9931              :       /* lock the buffer */
    9932           11 :       bm_lock_buffer_guard pbuf_guard(pbuf);
    9933              : 
    9934           11 :       if (!pbuf_guard.is_locked()) {
    9935            0 :          return pbuf_guard.get_status();
    9936              :       }
    9937              : 
    9938              :       /* calculate some shorthands */
    9939           11 :       BUFFER_HEADER *pheader = pbuf->buffer_header;
    9940              : 
    9941              : #if 0
    9942              :       status = bm_validate_buffer_locked(pbuf);
    9943              :       if (status != BM_SUCCESS) {
    9944              :          printf("bm_send_event: corrupted 111!\n");
    9945              :          abort();
    9946              :       }
    9947              : #endif
    9948              : 
    9949              :       /* check if buffer is large enough */
    9950           11 :       if (total_size >= (size_t)pheader->size) {
    9951            0 :          pbuf_guard.unlock(); // unlock before cm_msg()
    9952            0 :          cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
    9953            0 :          return BM_NO_MEMORY;
    9954              :       }
    9955              : 
    9956           11 :       status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, total_size, false);
    9957              : 
    9958           11 :       if (status != BM_SUCCESS) {
    9959              :          // implicit unlock
    9960            0 :          return status;
    9961              :       }
    9962              : 
    9963              : #if 0
    9964              :       status = bm_validate_buffer_locked(pbuf);
    9965              :       if (status != BM_SUCCESS) {
    9966              :          printf("bm_send_event: corrupted 222!\n");
    9967              :          abort();
    9968              :       }
    9969              : #endif
    9970              : 
    9971           11 :       int old_write_pointer = pheader->write_pointer;
    9972              : 
    9973           11 :       bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
    9974              : 
    9975              :       /* write pointer was incremented, but there should
    9976              :        * always be some free space in the buffer and the
    9977              :        * write pointer should never cacth up to the read pointer:
    9978              :        * the rest of the code gets confused this happens (buffer 100% full)
    9979              :        * as it is write_pointer == read_pointer can be either
    9980              :        * 100% full or 100% empty. My solution: never fill
    9981              :        * the buffer to 100% */
    9982           11 :       assert(pheader->write_pointer != pheader->read_pointer);
    9983              : 
    9984              :       /* send wake up messages to all clients that want this event */
    9985              :       int i;
    9986           22 :       for (i = 0; i < pheader->max_client_index; i++) {
    9987           11 :          BUFFER_CLIENT *pc = pheader->client + i;
    9988           11 :          int request_id = bm_find_first_request_locked(pc, pevent);
    9989           11 :          bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
    9990              :       }
    9991              : 
    9992              : #if 0
    9993              :       status = bm_validate_buffer_locked(pbuf);
    9994              :       if (status != BM_SUCCESS) {
    9995              :          printf("bm_send_event: corrupted 333!\n");
    9996              :          abort();
    9997              :       }
    9998              : #endif
    9999              : 
   10000              :       /* update statistics */
   10001           11 :       pheader->num_in_events++;
   10002           11 :       pbuf->count_sent += 1;
   10003           11 :       pbuf->bytes_sent += total_size;
   10004           11 :    }
   10005              : #endif                          /* LOCAL_ROUTINES */
   10006              : 
   10007           11 :    return BM_SUCCESS;
   10008              : }
   10009              : 
   10010            0 : static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
   10011              : {
   10012              :    //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
   10013              : 
   10014            0 :    DWORD time_start = ss_millitime();
   10015            0 :    DWORD time_end = time_start + timeout_msec;
   10016              :    
   10017            0 :    int xtimeout_msec = timeout_msec;
   10018              : 
   10019              :    while (1) {
   10020            0 :       if (timeout_msec == BM_WAIT) {
   10021            0 :          xtimeout_msec = 1000;
   10022            0 :       } else if (timeout_msec == BM_NO_WAIT) {
   10023            0 :          xtimeout_msec = BM_NO_WAIT;
   10024              :       } else {
   10025            0 :          if (xtimeout_msec > 1000) {
   10026            0 :             xtimeout_msec = 1000;
   10027              :          }
   10028              :       }
   10029              : 
   10030            0 :       int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
   10031              :       
   10032              :       //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
   10033              : 
   10034            0 :       if (status == BM_ASYNC_RETURN) {
   10035            0 :          if (timeout_msec == BM_WAIT) {
   10036              :             // BM_WAIT means wait forever
   10037            0 :             continue;
   10038            0 :          } else if (timeout_msec == BM_NO_WAIT) {
   10039              :             // BM_NO_WAIT means do not wait
   10040            0 :             return status;
   10041              :          } else {
   10042            0 :             DWORD now = ss_millitime();
   10043            0 :             if (now >= time_end) {
   10044              :                // timeout, return BM_ASYNC_RETURN
   10045            0 :                return status;
   10046              :             }
   10047              : 
   10048            0 :             DWORD remain = time_end - now;
   10049              : 
   10050            0 :             if (remain < (DWORD)xtimeout_msec) {
   10051            0 :                xtimeout_msec = remain;
   10052              :             }
   10053              : 
   10054              :             // keep asking for event...
   10055            0 :             continue;
   10056            0 :          }
   10057            0 :       } else if (status == BM_SUCCESS) {
   10058              :          // success, return BM_SUCCESS
   10059            0 :          return status;
   10060              :       } else {
   10061              :          // error
   10062            0 :          return status;
   10063              :       }
   10064            0 :    }
   10065              : }
   10066              : 
   10067              : /********************************************************************/
   10068              : /**
   10069              : Empty write cache.
   10070              : This function should be used if events in the write cache
   10071              : should be visible to the consumers immediately. It should be called at the
   10072              : end of each run, otherwise events could be kept in the write buffer and will
   10073              : flow to the data of the next run.
   10074              : @param buffer_handle Buffer handle obtained via bm_open_buffer() or 0 to flush data in the mserver event socket
   10075              : @param timeout_msec Timeout waiting for free space in the event buffer.
   10076              : If BM_WAIT, wait forever. If BM_NO_WAIT, the function returns
   10077              : immediately with a value of BM_ASYNC_RETURN without writing the cache.
   10078              : @return BM_SUCCESS, BM_INVALID_HANDLE<br>
   10079              : BM_ASYNC_RETURN Routine called with async_flag == BM_NO_WAIT
   10080              : and buffer has not enough space to receive cache<br>
   10081              : BM_NO_MEMORY Event is too large for network buffer or event buffer.
   10082              : One has to increase the event buffer size "/Experiment/Buffer sizes/SYSTEM"
   10083              : and/or /Experiment/MAX_EVENT_SIZE in ODB.
   10084              : */
   10085              : #ifdef LOCAL_ROUTINES
   10086            0 : static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
   10087              : {
   10088              :    // NB we come here with write cache locked and buffer locked.
   10089              : 
   10090              :    {
   10091            0 :       INT status = 0;
   10092              : 
   10093              :       //printf("bm_flush_cache_locked!\n");
   10094              : 
   10095            0 :       BUFFER* pbuf = pbuf_guard.get_pbuf();
   10096            0 :       BUFFER_HEADER* pheader = pbuf->buffer_header;
   10097              : 
   10098              :       //printf("bm_flush_cache_locked: buffer %s, cache rp %zu, wp %zu, timeout %d msec\n", pbuf->buffer_name, pbuf->write_cache_rp, pbuf->write_cache_wp, timeout_msec);
   10099              : 
   10100            0 :       int old_write_pointer = pheader->write_pointer;
   10101              : 
   10102              :       int request_id[MAX_CLIENTS];
   10103            0 :       for (int i = 0; i < pheader->max_client_index; i++) {
   10104            0 :          request_id[i] = -1;
   10105              :       }
   10106              :          
   10107            0 :       size_t ask_rp = pbuf->write_cache_rp;
   10108            0 :       size_t ask_wp = pbuf->write_cache_wp;
   10109              : 
   10110            0 :       if (ask_wp == 0) { // nothing to do
   10111            0 :          return BM_SUCCESS;
   10112              :       }
   10113              : 
   10114            0 :       if (ask_rp == ask_wp) { // nothing to do
   10115            0 :          return BM_SUCCESS;
   10116              :       }
   10117              : 
   10118            0 :       assert(ask_rp < ask_wp);
   10119              : 
   10120            0 :       size_t ask_free = ALIGN8(ask_wp - ask_rp);
   10121              : 
   10122            0 :       if (ask_free == 0) { // nothing to do
   10123            0 :          return BM_SUCCESS;
   10124              :       }
   10125              : 
   10126              : #if 0
   10127              :       status = bm_validate_buffer_locked(pbuf);
   10128              :       if (status != BM_SUCCESS) {
   10129              :          printf("bm_flush_cache: corrupted 111!\n");
   10130              :          abort();
   10131              :       }
   10132              : #endif
   10133              : 
   10134            0 :       status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, ask_free, true);
   10135              : 
   10136            0 :       if (status != BM_SUCCESS) {
   10137            0 :          return status;
   10138              :       }
   10139              : 
   10140              :       // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
   10141              :       //
   10142              :       // wait_for_free_space() will sleep with all locks released,
   10143              :       // during this time, another thread may call bm_send_event() that will
   10144              :       // add one or more events to the write cache and after wait_for_free_space()
   10145              :       // returns, size of data in cache will be bigger than the amount
   10146              :       // of free space we requested. so we need to keep track of how
   10147              :       // much data we write to the buffer and ask for more data
   10148              :       // if we run short. This is the reason for the big loop
   10149              :       // around wait_for_free_space(). We ask for slightly too little free
   10150              :       // space to make sure all this code is always used and does work. K.O.
   10151              : 
   10152            0 :       if (pbuf->write_cache_wp == 0) {
   10153              :          /* somebody emptied the cache while we were inside bm_wait_for_free_space */
   10154            0 :          return BM_SUCCESS;
   10155              :       }
   10156              : 
   10157              :       //size_t written = 0;
   10158            0 :       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
   10159              :          /* loop over all events in cache */
   10160              : 
   10161            0 :          const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
   10162            0 :          size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
   10163            0 :          size_t total_size = ALIGN8(event_size);
   10164              : 
   10165              : #if 0
   10166              :          printf("bm_flush_cache: cache size %d, wp %d, rp %d, event data_size %d, event_size %d, total_size %d, free %d, written %d\n",
   10167              :                 int(pbuf->write_cache_size),
   10168              :                 int(pbuf->write_cache_wp),
   10169              :                 int(pbuf->write_cache_rp),
   10170              :                 int(pevent->data_size),
   10171              :                 int(event_size),
   10172              :                 int(total_size),
   10173              :                 int(ask_free),
   10174              :                 int(written));
   10175              : #endif
   10176              : 
   10177              :          // check for crazy event size
   10178            0 :          assert(total_size >= sizeof(EVENT_HEADER));
   10179            0 :          assert(total_size <= (size_t)pheader->size);
   10180              : 
   10181            0 :          bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
   10182              : 
   10183              :          /* update statistics */
   10184            0 :          pheader->num_in_events++;
   10185            0 :          pbuf->count_sent += 1;
   10186            0 :          pbuf->bytes_sent += total_size;
   10187              : 
   10188              :          /* see comment for the same code in bm_send_event().
   10189              :           * We make sure the buffer is never 100% full */
   10190            0 :          assert(pheader->write_pointer != pheader->read_pointer);
   10191              : 
   10192              :          /* check if anybody has a request for this event */
   10193            0 :          for (int i = 0; i < pheader->max_client_index; i++) {
   10194            0 :             BUFFER_CLIENT *pc = pheader->client + i;
   10195            0 :             int r = bm_find_first_request_locked(pc, pevent);
   10196            0 :             if (r >= 0) {
   10197            0 :                request_id[i] = r;
   10198              :             }
   10199              :          }
   10200              :             
   10201              :          /* this loop does not loop forever because rp
   10202              :           * is monotonously incremented here. write_cache_wp does
   10203              :           * not change */
   10204              : 
   10205            0 :          pbuf->write_cache_rp += total_size;
   10206              :          //written += total_size;
   10207              : 
   10208            0 :          assert(pbuf->write_cache_rp > 0);
   10209            0 :          assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
   10210            0 :          assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
   10211              :       }
   10212              : 
   10213              :       /* the write cache is now empty */
   10214            0 :       assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
   10215            0 :       pbuf->write_cache_wp = 0;
   10216            0 :       pbuf->write_cache_rp = 0;
   10217              : 
   10218              :       /* check which clients are waiting */
   10219            0 :       for (int i = 0; i < pheader->max_client_index; i++) {
   10220            0 :          BUFFER_CLIENT *pc = pheader->client + i;
   10221            0 :          bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
   10222              :       }
   10223              :    }
   10224              : 
   10225            0 :    return BM_SUCCESS;
   10226              : }
   10227              : 
   10228              : #endif /* LOCAL_ROUTINES */
   10229              : 
   10230            0 : INT bm_flush_cache(int buffer_handle, int timeout_msec)
   10231              : {
   10232            0 :    if (rpc_is_remote()) {
   10233            0 :       return bm_flush_cache_rpc(buffer_handle, timeout_msec);
   10234              :    }
   10235              : 
   10236              : #ifdef LOCAL_ROUTINES
   10237              :    {
   10238            0 :       INT status = 0;
   10239              : 
   10240              :       //printf("bm_flush_cache!\n");
   10241              : 
   10242            0 :       BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
   10243              : 
   10244            0 :       if (!pbuf)
   10245            0 :          return status;
   10246              : 
   10247            0 :       if (pbuf->write_cache_size == 0)
   10248            0 :          return BM_SUCCESS;
   10249              : 
   10250            0 :       status = bm_lock_buffer_write_cache(pbuf);
   10251              : 
   10252            0 :       if (status != BM_SUCCESS)
   10253            0 :          return status;
   10254              : 
   10255              :       /* check if anything needs to be flushed */
   10256            0 :       if (pbuf->write_cache_wp == 0) {
   10257            0 :          pbuf->write_cache_mutex.unlock();
   10258            0 :          return BM_SUCCESS;
   10259              :       }
   10260              : 
   10261              :       /* lock the buffer */
   10262            0 :       bm_lock_buffer_guard pbuf_guard(pbuf);
   10263              : 
   10264            0 :       if (!pbuf_guard.is_locked())
   10265            0 :          return pbuf_guard.get_status();
   10266              : 
   10267            0 :       status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
   10268              : 
   10269              :       /* unlock in correct order */
   10270              : 
   10271            0 :       if (pbuf_guard.is_locked()) {
   10272              :          // check if bm_wait_for_free_space() failed to relock the buffer
   10273            0 :          pbuf_guard.unlock();
   10274              :       }
   10275              : 
   10276            0 :       pbuf->write_cache_mutex.unlock();
   10277              : 
   10278            0 :       return status;
   10279            0 :    }
   10280              : #endif                          /* LOCAL_ROUTINES */
   10281              : 
   10282              :    return BM_SUCCESS;
   10283              : }
   10284              : 
   10285              : #ifdef LOCAL_ROUTINES
   10286              : 
   10287            0 : static INT bm_read_buffer(BUFFER *pbuf, INT buffer_handle, void **bufptr, void *buf, INT *buf_size, std::vector<char> *vecptr, int timeout_msec, int convert_flags, BOOL dispatch) {
   10288            0 :    INT status = BM_SUCCESS;
   10289              : 
   10290            0 :    int max_size = 0;
   10291            0 :    if (buf_size) {
   10292            0 :       max_size = *buf_size;
   10293            0 :       *buf_size = 0;
   10294              :    }
   10295              : 
   10296              :    //printf("bm_read_buffer: [%s] timeout %d, conv %d, ptr %p, buf %p, disp %d\n", pbuf->buffer_name, timeout_msec, convert_flags, bufptr, buf, dispatch);
   10297              : 
   10298            0 :    bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
   10299              : 
   10300              :    // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
   10301              : 
   10302              :    /* look if there is anything in the cache */
   10303            0 :    if (pbuf->read_cache_size > 0) {
   10304              : 
   10305            0 :       status = bm_lock_buffer_read_cache(pbuf);
   10306              : 
   10307            0 :       if (status != BM_SUCCESS)
   10308            0 :          return status;
   10309              : 
   10310            0 :       if (pbuf->read_cache_wp == 0) {
   10311              : 
   10312              :          // lock buffer for the first time
   10313              : 
   10314            0 :          if (!pbuf_guard.relock()) {
   10315            0 :             pbuf->read_cache_mutex.unlock();
   10316            0 :             return pbuf_guard.get_status();
   10317              :          }
   10318              : 
   10319            0 :          status = bm_fill_read_cache_locked(pbuf_guard, timeout_msec);
   10320            0 :          if (status != BM_SUCCESS) {
   10321              :             // unlock in correct order
   10322            0 :             if (pbuf_guard.is_locked()) {
   10323              :                // check if bm_wait_for_more_events() failed to relock the buffer
   10324            0 :                pbuf_guard.unlock();
   10325              :             }
   10326            0 :             pbuf->read_cache_mutex.unlock();
   10327            0 :             return status;
   10328              :          }
   10329              : 
   10330              :          // buffer remains locked here
   10331              :       }
   10332              :       EVENT_HEADER *pevent;
   10333              :       int event_size;
   10334              :       int total_size;
   10335            0 :       if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
   10336            0 :          if (pbuf_guard.is_locked()) {
   10337              :             // do not need to keep the event buffer locked
   10338              :             // when reading from the read cache
   10339            0 :             pbuf_guard.unlock();
   10340              :          }
   10341              :          //printf("bm_read_buffer: [%s] async %d, conv %d, ptr %p, buf %p, disp %d, total_size %d, read from cache %d %d %d\n", pbuf->buffer_name, async_flag, convert_flags, bufptr, buf, dispatch, total_size, pbuf->read_cache_size, pbuf->read_cache_rp, pbuf->read_cache_wp);
   10342            0 :          status = BM_SUCCESS;
   10343            0 :          if (buf) {
   10344            0 :             if (event_size > max_size) {
   10345            0 :                cm_msg(MERROR, "bm_read_buffer", "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size, event_size, pbuf->buffer_name);
   10346            0 :                event_size = max_size;
   10347            0 :                status = BM_TRUNCATED;
   10348              :             }
   10349              : 
   10350            0 :             memcpy(buf, pevent, event_size);
   10351              : 
   10352            0 :             if (buf_size) {
   10353            0 :                *buf_size = event_size;
   10354              :             }
   10355            0 :             if (convert_flags) {
   10356            0 :                bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
   10357              :             }
   10358            0 :          } else if (bufptr) {
   10359            0 :             *bufptr = malloc(event_size);
   10360            0 :             memcpy(*bufptr, pevent, event_size);
   10361            0 :             status = BM_SUCCESS;
   10362            0 :          } else if (vecptr) {
   10363            0 :             vecptr->resize(0);
   10364            0 :             char* cptr = (char*)pevent;
   10365            0 :             vecptr->assign(cptr, cptr+event_size);
   10366              :          }
   10367            0 :          bm_incr_read_cache_locked(pbuf, total_size);
   10368            0 :          pbuf->read_cache_mutex.unlock();
   10369            0 :          if (dispatch) {
   10370              :             // FIXME need to protect currently dispatched event against
   10371              :             // another thread overwriting it by refilling the read cache
   10372            0 :             bm_dispatch_event(buffer_handle, pevent);
   10373            0 :             return BM_MORE_EVENTS;
   10374              :          }
   10375              :          // buffer is unlocked here
   10376            0 :          return status;
   10377              :       }
   10378            0 :       pbuf->read_cache_mutex.unlock();
   10379              :    }
   10380              : 
   10381              :    /* we come here if the read cache is disabled */
   10382              :    /* we come here if the next event is too big to fit into the read cache */
   10383              : 
   10384            0 :    if (!pbuf_guard.is_locked()) {
   10385            0 :       if (!pbuf_guard.relock())
   10386            0 :          return pbuf_guard.get_status();
   10387              :    }
   10388              : 
   10389            0 :    EVENT_HEADER *event_buffer = NULL;
   10390              : 
   10391            0 :    BUFFER_HEADER *pheader = pbuf->buffer_header;
   10392              : 
   10393            0 :    BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
   10394              : 
   10395              :    while (1) {
   10396              :       /* loop over events in the event buffer */
   10397              : 
   10398            0 :       status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, FALSE);
   10399              : 
   10400            0 :       if (status != BM_SUCCESS) {
   10401              :          // implicit unlock
   10402            0 :          return status;
   10403              :       }
   10404              : 
   10405              :       /* check if event at current read pointer matches a request */
   10406              : 
   10407              :       EVENT_HEADER *pevent;
   10408              :       int event_size;
   10409              :       int total_size;
   10410              : 
   10411            0 :       status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
   10412            0 :       if (status == BM_CORRUPTED) {
   10413              :          // implicit unlock
   10414            0 :          return status;
   10415            0 :       } else if (status != BM_SUCCESS) {
   10416              :          /* event buffer is empty */
   10417            0 :          break;
   10418              :       }
   10419              : 
   10420            0 :       BOOL is_requested = bm_check_requests(pc, pevent);
   10421              : 
   10422            0 :       if (is_requested) {
   10423              :          //printf("bm_read_buffer: [%s] async %d, conv %d, ptr %p, buf %p, disp %d, total_size %d, read from buffer, cache %d %d %d\n", pheader->name, async_flag, convert_flags, bufptr, buf, dispatch, total_size, pbuf->read_cache_size, pbuf->read_cache_rp, pbuf->read_cache_wp);
   10424              : 
   10425            0 :          status = BM_SUCCESS;
   10426              : 
   10427            0 :          if (buf) {
   10428            0 :             if (event_size > max_size) {
   10429            0 :                cm_msg(MERROR, "bm_read_buffer",
   10430              :                       "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
   10431            0 :                       event_size, pheader->name);
   10432            0 :                event_size = max_size;
   10433            0 :                status = BM_TRUNCATED;
   10434              :             }
   10435              : 
   10436            0 :             bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
   10437              : 
   10438            0 :             if (buf_size) {
   10439            0 :                *buf_size = event_size;
   10440              :             }
   10441              : 
   10442            0 :             if (convert_flags) {
   10443            0 :                bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
   10444              :             }
   10445              : 
   10446            0 :             pbuf->count_read++;
   10447            0 :             pbuf->bytes_read += event_size;
   10448            0 :          } else if (dispatch || bufptr) {
   10449            0 :             assert(event_buffer == NULL); // make sure we only come here once
   10450            0 :             event_buffer = (EVENT_HEADER *) malloc(event_size);
   10451            0 :             bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) event_buffer, event_size);
   10452            0 :             pbuf->count_read++;
   10453            0 :             pbuf->bytes_read += event_size;
   10454            0 :          } else if (vecptr) {
   10455            0 :             bm_read_from_buffer_locked(pheader, pc->read_pointer, vecptr, event_size);
   10456            0 :             pbuf->count_read++;
   10457            0 :             pbuf->bytes_read += event_size;
   10458              :          }
   10459              : 
   10460            0 :          int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
   10461            0 :          pc->read_pointer = new_read_pointer;
   10462              : 
   10463            0 :          pheader->num_out_events++;
   10464              :          /* exit loop over events */
   10465            0 :          break;
   10466              :       }
   10467              : 
   10468            0 :       int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
   10469            0 :       pc->read_pointer = new_read_pointer;
   10470            0 :       pheader->num_out_events++;
   10471            0 :    }
   10472              : 
   10473              :    /*
   10474              :      If read pointer has been changed, it may have freed up some space
   10475              :      for waiting producers. So check if free space is now more than 50%
   10476              :      of the buffer size and wake waiting producers.
   10477              :    */
   10478              : 
   10479            0 :    bm_wakeup_producers_locked(pheader, pc);
   10480              : 
   10481            0 :    pbuf_guard.unlock();
   10482              : 
   10483            0 :    if (dispatch && event_buffer) {
   10484            0 :       bm_dispatch_event(buffer_handle, event_buffer);
   10485            0 :       free(event_buffer);
   10486            0 :       event_buffer = NULL;
   10487            0 :       return BM_MORE_EVENTS;
   10488              :    }
   10489              : 
   10490            0 :    if (bufptr && event_buffer) {
   10491            0 :       *bufptr = event_buffer;
   10492            0 :       event_buffer = NULL;
   10493            0 :       status = BM_SUCCESS;
   10494              :    }
   10495              : 
   10496            0 :    if (event_buffer) {
   10497            0 :       free(event_buffer);
   10498            0 :       event_buffer = NULL;
   10499              :    }
   10500              : 
   10501            0 :    return status;
   10502            0 : }
   10503              : 
   10504              : #endif
   10505              : 
   10506            0 : static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
   10507              : {
   10508              :    //printf("bm_receive_event_rpc: handle %d, buf %p, pevent %p, pvec %p, timeout %d, max_event_size %d\n", buffer_handle, buf, ppevent, pvec, timeout_msec, _bm_max_event_size);
   10509              : 
   10510            0 :    assert(_bm_max_event_size > sizeof(EVENT_HEADER));
   10511              : 
   10512            0 :    void *xbuf = NULL;
   10513            0 :    int xbuf_size = 0;
   10514              : 
   10515            0 :    if (buf) {
   10516            0 :       xbuf = buf;
   10517            0 :       xbuf_size = *buf_size;
   10518            0 :    } else if (ppevent) {
   10519            0 :       *ppevent = (EVENT_HEADER*)malloc(_bm_max_event_size);
   10520            0 :       xbuf_size = _bm_max_event_size;
   10521            0 :    } else if (pvec) {
   10522            0 :       pvec->resize(_bm_max_event_size);
   10523            0 :       xbuf = pvec->data();
   10524            0 :       xbuf_size = pvec->size();
   10525              :    } else {
   10526            0 :       assert(!"incorrect call to bm_receivent_event_rpc()");
   10527              :    }
   10528              : 
   10529              :    int status;
   10530            0 :    DWORD time_start = ss_millitime();
   10531            0 :    DWORD time_end = time_start + timeout_msec;
   10532              :    
   10533            0 :    int xtimeout_msec = timeout_msec;
   10534              : 
   10535            0 :    int zbuf_size = xbuf_size;
   10536              : 
   10537              :    while (1) {
   10538            0 :       if (timeout_msec == BM_WAIT) {
   10539            0 :          xtimeout_msec = 1000;
   10540            0 :       } else if (timeout_msec == BM_NO_WAIT) {
   10541            0 :          xtimeout_msec = BM_NO_WAIT;
   10542              :       } else {
   10543            0 :          if (xtimeout_msec > 1000) {
   10544            0 :             xtimeout_msec = 1000;
   10545              :          }
   10546              :       }
   10547              : 
   10548            0 :       zbuf_size = xbuf_size;
   10549              : 
   10550            0 :       status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, xbuf, &zbuf_size, xtimeout_msec);
   10551              :       
   10552              :       //printf("bm_receive_event_rpc: handle %d, timeout %d, status %d, size %d in, %d out, via RPC_BM_RECEIVE_EVENT\n", buffer_handle, xtimeout_msec, status, xbuf_size, zbuf_size);
   10553              : 
   10554            0 :       if (status == BM_ASYNC_RETURN) {
   10555            0 :          if (timeout_msec == BM_WAIT) {
   10556              :             // BM_WAIT means wait forever
   10557            0 :             continue;
   10558            0 :          } else if (timeout_msec == BM_NO_WAIT) {
   10559              :             // BM_NO_WAIT means do not wait
   10560            0 :             break;
   10561              :          } else {
   10562            0 :             DWORD now = ss_millitime();
   10563            0 :             if (now >= time_end) {
   10564              :                // timeout, return BM_ASYNC_RETURN
   10565            0 :                break;
   10566              :             }
   10567              : 
   10568            0 :             DWORD remain = time_end - now;
   10569              : 
   10570            0 :             if (remain < (DWORD)xtimeout_msec) {
   10571            0 :                xtimeout_msec = remain;
   10572              :             }
   10573              : 
   10574              :             // keep asking for event...
   10575            0 :             continue;
   10576            0 :          }
   10577            0 :       } else if (status == BM_SUCCESS) {
   10578              :          // success, return BM_SUCCESS
   10579            0 :          break;
   10580              :       }
   10581              : 
   10582              :       // RPC error
   10583              :          
   10584            0 :       if (buf) {
   10585            0 :          *buf_size = 0;
   10586            0 :       } else if (ppevent) {
   10587            0 :          free(*ppevent);
   10588            0 :          *ppevent = NULL;
   10589            0 :       } else if (pvec) {
   10590            0 :          pvec->resize(0);
   10591              :       } else {
   10592            0 :          assert(!"incorrect call to bm_receivent_event_rpc()");
   10593              :       }
   10594              :       
   10595            0 :       return status;
   10596            0 :    }
   10597              : 
   10598              :    // status is BM_SUCCESS or BM_ASYNC_RETURN
   10599              : 
   10600            0 :    if (buf) {
   10601            0 :       *buf_size = zbuf_size;
   10602            0 :    } else if (ppevent) {
   10603              :       // nothing to do
   10604              :       // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
   10605            0 :    } else if (pvec) {
   10606            0 :       pvec->resize(zbuf_size);
   10607              :    } else {
   10608            0 :       assert(!"incorrect call to bm_receivent_event_rpc()");
   10609              :    }
   10610              : 
   10611            0 :    return status;
   10612              : }
   10613              : 
   10614              : /********************************************************************/
   10615              : /**
   10616              : Receives events directly.
   10617              : This function is an alternative way to receive events without
   10618              : a main loop.
   10619              : 
   10620              : It can be used in analysis systems which actively receive events,
   10621              : rather than using callbacks. A analysis package could for example contain its own
   10622              : command line interface. A command
   10623              : like "receive 1000 events" could make it necessary to call bm_receive_event()
   10624              : 1000 times in a row to receive these events and then return back to the
   10625              : command line prompt.
   10626              : The according bm_request_event() call contains NULL as the
   10627              : callback routine to indicate that bm_receive_event() is called to receive
   10628              : events.
   10629              : \code
   10630              : #include <stdio.h>
   10631              : #include "midas.h"
   10632              : void process_event(EVENT_HEADER *pheader)
   10633              : {
   10634              :  printf("Received event #%d\r",
   10635              :  pheader->serial_number);
   10636              : }
   10637              : main()
   10638              : {
   10639              :   INT status, request_id;
   10640              :   HNDLE hbuf;
   10641              :   char event_buffer[1000];
   10642              :   status = cm_connect_experiment("", "Sample",
   10643              :   "Simple Analyzer", NULL);
   10644              :   if (status != CM_SUCCESS)
   10645              :    return 1;
   10646              :   bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
   10647              :   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
   10648              : 
   10649              :   do
   10650              :   {
   10651              :    size = sizeof(event_buffer);
   10652              :    status = bm_receive_event(hbuf, event_buffer, &size, BM_NO_WAIT);
   10653              :   if (status == CM_SUCCESS)
   10654              :    process_event((EVENT_HEADER *) event_buffer);
   10655              :    <...do something else...>
   10656              :    status = cm_yield(0);
   10657              :   } while (status != RPC_SHUTDOWN &&
   10658              :   status != SS_ABORT);
   10659              :   cm_disconnect_experiment();
   10660              :   return 0;
   10661              : }
   10662              : \endcode
   10663              : @param buffer_handle buffer handle
   10664              : @param destination destination address where event is written to
   10665              : @param buf_size size of destination buffer on input, size of event plus
   10666              : header on return.
   10667              : @param timeout_msec Wait so many millisecond for new data. Special values: BM_WAIT: wait forever, BM_NO_WAIT: do not wait, return BM_ASYNC_RETURN if no data is immediately available
   10668              : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
   10669              : BM_TRUNCATED   The event is larger than the destination buffer and was
   10670              :                therefore truncated <br>
   10671              : BM_ASYNC_RETURN No event available
   10672              : */
   10673            0 : INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
   10674              :    //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
   10675            0 :    if (rpc_is_remote()) {
   10676            0 :       return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
   10677              :    }
   10678              : #ifdef LOCAL_ROUTINES
   10679              :    {
   10680            0 :       INT status = BM_SUCCESS;
   10681              : 
   10682            0 :       BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
   10683              : 
   10684            0 :       if (!pbuf)
   10685            0 :          return status;
   10686              : 
   10687            0 :       int convert_flags = rpc_get_convert_flags();
   10688              : 
   10689            0 :       status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
   10690              :       //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
   10691            0 :       return status;
   10692              :    }
   10693              : #else                           /* LOCAL_ROUTINES */
   10694              : 
   10695              :    return BM_SUCCESS;
   10696              : #endif
   10697              : }
   10698              : 
   10699              : /********************************************************************/
   10700              : /**
   10701              : Receives events directly.
   10702              : This function is an alternative way to receive events without
   10703              : a main loop.
   10704              : 
   10705              : It can be used in analysis systems which actively receive events,
   10706              : rather than using callbacks. A analysis package could for example contain its own
   10707              : command line interface. A command
   10708              : like "receive 1000 events" could make it necessary to call bm_receive_event()
   10709              : 1000 times in a row to receive these events and then return back to the
   10710              : command line prompt.
   10711              : The according bm_request_event() call contains NULL as the
   10712              : callback routine to indicate that bm_receive_event() is called to receive
   10713              : events.
   10714              : \code
   10715              : #include <stdio.h>
   10716              : #include "midas.h"
   10717              : void process_event(EVENT_HEADER *pheader)
   10718              : {
   10719              :  printf("Received event #%d\r",
   10720              :  pheader->serial_number);
   10721              : }
   10722              : main()
   10723              : {
   10724              :   INT status, request_id;
   10725              :   HNDLE hbuf;
   10726              :   char event_buffer[1000];
   10727              :   status = cm_connect_experiment("", "Sample",
   10728              :   "Simple Analyzer", NULL);
   10729              :   if (status != CM_SUCCESS)
   10730              :    return 1;
   10731              :   bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
   10732              :   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
   10733              : 
   10734              :   do
   10735              :   {
   10736              :    size = sizeof(event_buffer);
   10737              :    status = bm_receive_event(hbuf, event_buffer, &size, BM_NO_WAIT);
   10738              :   if (status == CM_SUCCESS)
   10739              :    process_event((EVENT_HEADER *) event_buffer);
   10740              :    <...do something else...>
   10741              :    status = cm_yield(0);
   10742              :   } while (status != RPC_SHUTDOWN &&
   10743              :   status != SS_ABORT);
   10744              :   cm_disconnect_experiment();
   10745              :   return 0;
   10746              : }
   10747              : \endcode
   10748              : @param buffer_handle buffer handle
   10749              : @param ppevent pointer to the received event pointer, event pointer should be free()ed to avoid memory leak
   10750              : @param timeout_msec Wait so many millisecond for new data. Special values: BM_WAIT: wait forever, BM_NO_WAIT: do not wait, return BM_ASYNC_RETURN if no data is immediately available
   10751              : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
   10752              : BM_ASYNC_RETURN No event available
   10753              : */
   10754            0 : INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec) {
   10755            0 :    if (rpc_is_remote()) {
   10756            0 :       return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
   10757              :    }
   10758              : #ifdef LOCAL_ROUTINES
   10759              :    {
   10760            0 :       INT status = BM_SUCCESS;
   10761              : 
   10762            0 :       BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
   10763              : 
   10764            0 :       if (!pbuf)
   10765            0 :          return status;
   10766              : 
   10767            0 :       int convert_flags = rpc_get_convert_flags();
   10768              : 
   10769            0 :       return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
   10770              :    }
   10771              : #else                           /* LOCAL_ROUTINES */
   10772              : 
   10773              :    return BM_SUCCESS;
   10774              : #endif
   10775              : }
   10776              : 
   10777              : /********************************************************************/
   10778              : /**
   10779              : Receives events directly.
   10780              : This function is an alternative way to receive events without
   10781              : a main loop.
   10782              : 
   10783              : It can be used in analysis systems which actively receive events,
   10784              : rather than using callbacks. A analysis package could for example contain its own
   10785              : command line interface. A command
   10786              : like "receive 1000 events" could make it necessary to call bm_receive_event()
   10787              : 1000 times in a row to receive these events and then return back to the
   10788              : command line prompt.
   10789              : The according bm_request_event() call contains NULL as the
   10790              : callback routine to indicate that bm_receive_event() is called to receive
   10791              : events.
   10792              : \code
   10793              : #include <stdio.h>
   10794              : #include "midas.h"
   10795              : void process_event(EVENT_HEADER *pheader)
   10796              : {
   10797              :  printf("Received event #%d\r",
   10798              :  pheader->serial_number);
   10799              : }
   10800              : main()
   10801              : {
   10802              :   INT status, request_id;
   10803              :   HNDLE hbuf;
   10804              :   char event_buffer[1000];
   10805              :   status = cm_connect_experiment("", "Sample",
   10806              :   "Simple Analyzer", NULL);
   10807              :   if (status != CM_SUCCESS)
   10808              :    return 1;
   10809              :   bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
   10810              :   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
   10811              : 
   10812              :   do
   10813              :   {
   10814              :    size = sizeof(event_buffer);
   10815              :    status = bm_receive_event(hbuf, event_buffer, &size, BM_NO_WAIT);
   10816              :   if (status == CM_SUCCESS)
   10817              :    process_event((EVENT_HEADER *) event_buffer);
   10818              :    <...do something else...>
   10819              :    status = cm_yield(0);
   10820              :   } while (status != RPC_SHUTDOWN &&
   10821              :   status != SS_ABORT);
   10822              :   cm_disconnect_experiment();
   10823              :   return 0;
   10824              : }
   10825              : \endcode
   10826              : @param buffer_handle buffer handle
   10827              : @param ppevent pointer to the received event pointer, event pointer should be free()ed to avoid memory leak
   10828              : @param timeout_msec Wait so many millisecond for new data. Special values: BM_WAIT: wait forever, BM_NO_WAIT: do not wait, return BM_ASYNC_RETURN if no data is immediately available
   10829              : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
   10830              : BM_ASYNC_RETURN No event available
   10831              : */
   10832            0 : INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
   10833            0 :    if (rpc_is_remote()) {
   10834            0 :       return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
   10835              :    }
   10836              : #ifdef LOCAL_ROUTINES
   10837              :    {
   10838            0 :       INT status = BM_SUCCESS;
   10839              : 
   10840            0 :       BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
   10841              : 
   10842            0 :       if (!pbuf)
   10843            0 :          return status;
   10844              : 
   10845            0 :       int convert_flags = rpc_get_convert_flags();
   10846              : 
   10847            0 :       return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
   10848              :    }
   10849              : #else /* LOCAL_ROUTINES */
   10850              :    return BM_SUCCESS;
   10851              : #endif
   10852              : }
   10853              : 
   10854              : #ifdef LOCAL_ROUTINES
   10855              : 
   10856            0 : static int bm_skip_event(BUFFER* pbuf)
   10857              : {
   10858              :    /* clear read cache */
   10859            0 :    if (pbuf->read_cache_size > 0) {
   10860              : 
   10861            0 :       int status = bm_lock_buffer_read_cache(pbuf);
   10862              : 
   10863            0 :       if (status != BM_SUCCESS)
   10864            0 :          return status;
   10865              : 
   10866            0 :       pbuf->read_cache_rp = 0;
   10867            0 :       pbuf->read_cache_wp = 0;
   10868              : 
   10869            0 :       pbuf->read_cache_mutex.unlock();
   10870              :    }
   10871              :    
   10872            0 :    bm_lock_buffer_guard pbuf_guard(pbuf);
   10873              : 
   10874            0 :    if (!pbuf_guard.is_locked())
   10875            0 :       return pbuf_guard.get_status();
   10876              :    
   10877            0 :    BUFFER_HEADER *pheader = pbuf->buffer_header;
   10878              :    
   10879              :    /* forward read pointer to global write pointer */
   10880            0 :    BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
   10881            0 :    pclient->read_pointer = pheader->write_pointer;
   10882              :    
   10883            0 :    return BM_SUCCESS;
   10884            0 : }
   10885              : 
   10886              : #endif /* LOCAL_ROUTINES */
   10887              : 
   10888              : /********************************************************************/
   10889              : /**
   10890              : Skip all events in current buffer.
   10891              : 
   10892              : Useful for single event displays to see the newest events
   10893              : @param buffer_handle      Handle of the buffer. Must be obtained
   10894              :                           via bm_open_buffer.
   10895              : @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR
   10896              : */
   10897            0 : INT bm_skip_event(INT buffer_handle) {
   10898            0 :    if (rpc_is_remote())
   10899            0 :       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
   10900              : 
   10901              : #ifdef LOCAL_ROUTINES
   10902              :    {
   10903            0 :       int status = 0;
   10904              : 
   10905            0 :       BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
   10906              : 
   10907            0 :       if (!pbuf)
   10908            0 :          return status;
   10909              : 
   10910            0 :       return bm_skip_event(pbuf);
   10911              :    }
   10912              : #endif
   10913              : 
   10914              :    return BM_SUCCESS;
   10915              : }
   10916              : 
   10917              : #ifdef LOCAL_ROUTINES
   10918              : /********************************************************************/
   10919              : /**
   10920              : Check a buffer if an event is available and call the dispatch function if found.
   10921              : @param buffer_name       Name of buffer
   10922              : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN,
   10923              :                     RPC_NET_ERROR
   10924              : */
   10925            0 : static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
   10926              :    //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
   10927              : 
   10928              :    /* return immediately if no callback routine is defined */
   10929            0 :    if (!pbuf->callback)
   10930            0 :       return BM_SUCCESS;
   10931              : 
   10932            0 :    return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
   10933              : }
   10934              : 
   10935              : /********************************************************************/
   10936              : /**
   10937              : Check a buffer if an event is available and call the dispatch function if found.
   10938              : @param buffer_name       Name of buffer
   10939              : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, BM_CORRUPTED, RPC_NET_ERROR
   10940              : */
   10941            0 : static INT bm_push_event(const char *buffer_name)
   10942              : {
   10943            0 :    std::vector<BUFFER*> mybuffers;
   10944              :    
   10945            0 :    gBuffersMutex.lock();
   10946            0 :    mybuffers = gBuffers;
   10947            0 :    gBuffersMutex.unlock();
   10948              :    
   10949            0 :    for (size_t i = 0; i < mybuffers.size(); i++) {
   10950            0 :       BUFFER *pbuf = mybuffers[i];
   10951            0 :       if (!pbuf || !pbuf->attached)
   10952            0 :          continue;
   10953              :       // FIXME: unlocked read access to pbuf->buffer_name!
   10954            0 :       if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
   10955            0 :          return bm_push_buffer(pbuf, i + 1);
   10956              :       }
   10957              :    }
   10958              : 
   10959            0 :    return BM_INVALID_HANDLE;
   10960            0 : }
   10961              : 
   10962              : #else
   10963              : 
   10964              : static INT bm_push_event(const char *buffer_name)
   10965              : {
   10966              :    return BM_SUCCESS;                                                  
   10967              : }
   10968              : 
   10969              : #endif /* LOCAL_ROUTINES */
   10970              : 
   10971              : /********************************************************************/
   10972              : /**
   10973              : Check if any requested event is waiting in a buffer
   10974              : @return TRUE             More events are waiting<br>
   10975              :         FALSE            No more events are waiting
   10976              : */
   10977            0 : INT bm_check_buffers() {
   10978              : #ifdef LOCAL_ROUTINES
   10979              :    {
   10980            0 :       INT status = 0;
   10981              :       BOOL bMore;
   10982              :       DWORD start_time;
   10983              :       //static DWORD last_time = 0;
   10984              : 
   10985              :       /* if running as a server, buffer checking is done by client
   10986              :          via ASYNC bm_receive_event */
   10987            0 :       if (rpc_is_mserver()) {
   10988            0 :          return FALSE;
   10989              :       }
   10990              : 
   10991            0 :       bMore = FALSE;
   10992            0 :       start_time = ss_millitime();
   10993              : 
   10994            0 :       std::vector<BUFFER*> mybuffers;
   10995              :       
   10996            0 :       gBuffersMutex.lock();
   10997            0 :       mybuffers = gBuffers;
   10998            0 :       gBuffersMutex.unlock();
   10999              : 
   11000              :       /* go through all buffers */
   11001            0 :       for (size_t idx = 0; idx < mybuffers.size(); idx++) {
   11002            0 :          BUFFER* pbuf = mybuffers[idx];
   11003              : 
   11004            0 :          if (!pbuf || !pbuf->attached)
   11005            0 :             continue;
   11006              : 
   11007              :          //int count_loops = 0;
   11008              :          while (1) {
   11009            0 :             if (pbuf->attached) {
   11010              :                /* one bm_push_event could cause a run stop and a buffer close, which
   11011              :                 * would crash the next call to bm_push_event(). So check for valid
   11012              :                 * buffer on each call */
   11013              : 
   11014              :                /* this is what happens:
   11015              :                 * bm_push_buffer() may call a user callback function
   11016              :                 * user callback function may indirectly call bm_close() of this buffer,
   11017              :                 * i.e. if it stops the run,
   11018              :                 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
   11019              :                 * here we will see pbuf->attched is false and quit this loop
   11020              :                 */
   11021              : 
   11022            0 :                status = bm_push_buffer(pbuf, idx + 1);
   11023              : 
   11024            0 :                if (status == BM_CORRUPTED) {
   11025            0 :                   return status;
   11026              :                }
   11027              : 
   11028              :                //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
   11029              : 
   11030            0 :                if (status != BM_MORE_EVENTS) {
   11031              :                   //DWORD t = ss_millitime() - start_time;
   11032              :                   //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
   11033            0 :                   break;
   11034              :                }
   11035              : 
   11036              :                // count_loops++;
   11037              :             }
   11038              : 
   11039              :             // NB: this code has a logic error: if 2 buffers always have data,
   11040              :             // this timeout will cause us to exit reading the 1st buffer
   11041              :             // after 1000 msec, then we read the 2nd buffer exactly once,
   11042              :             // and exit the loop because the timeout is still active -
   11043              :             // we did not reset "start_time" when we started reading
   11044              :             // from the 2nd buffer. Result is that we always read all
   11045              :             // the data in a loop from the 1st buffer, but read just
   11046              :             // one event from the 2nd buffer, resulting in severe unfairness.
   11047              : 
   11048              :             /* stop after one second */
   11049            0 :             DWORD t = ss_millitime() - start_time;
   11050            0 :             if (t > 1000) {
   11051              :                //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
   11052            0 :                bMore = TRUE;
   11053            0 :                break;
   11054              :             }
   11055            0 :          }
   11056              :       }
   11057              : 
   11058              :       //last_time = start_time;
   11059              : 
   11060            0 :       return bMore;
   11061              : 
   11062            0 :    }
   11063              : #else                           /* LOCAL_ROUTINES */
   11064              : 
   11065              :    return FALSE;
   11066              : 
   11067              : #endif
   11068              : }
   11069              : 
   11070              : /********************************************************************/
   11071            0 : static INT bm_notify_client(const char *buffer_name, int client_socket)
   11072              : /********************************************************************\
   11073              : 
   11074              :   Routine: bm_notify_client
   11075              : 
   11076              :   Purpose: Called by cm_dispatch_ipc. Send an event notification to
   11077              :            the connected client. Used by mserver to relay the BM_MSG
   11078              :            buffer message from local UDP socket to the remote connected client.
   11079              : 
   11080              :   Input:
   11081              :     char  *buffer_name      Name of buffer
   11082              :     int   client_socket     Network socket to client
   11083              : 
   11084              :   Output:
   11085              :     none
   11086              : 
   11087              :   Function value:
   11088              :     BM_SUCCESS              Successful completion
   11089              : 
   11090              : \********************************************************************/
   11091              : {
   11092              :    static DWORD last_time = 0;
   11093            0 :    DWORD now = ss_millitime();
   11094              : 
   11095              :    //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
   11096              : 
   11097            0 :    BUFFER* fbuf = NULL;
   11098              : 
   11099            0 :    gBuffersMutex.lock();
   11100              : 
   11101            0 :    for (size_t i = 0; i < gBuffers.size(); i++) {
   11102            0 :       BUFFER* pbuf = gBuffers[i];
   11103            0 :       if (!pbuf || !pbuf->attached)
   11104            0 :          continue;
   11105            0 :       if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
   11106            0 :          fbuf = pbuf;
   11107            0 :          break;
   11108              :       }
   11109              :    }
   11110              : 
   11111            0 :    gBuffersMutex.unlock();
   11112              : 
   11113            0 :    if (!fbuf)
   11114            0 :       return BM_INVALID_HANDLE;
   11115              : 
   11116              :    /* don't send notification if client has no callback defined
   11117              :       to receive events -> client calls bm_receive_event manually */
   11118            0 :    if (!fbuf->callback)
   11119            0 :       return DB_SUCCESS;
   11120              : 
   11121            0 :    int convert_flags = rpc_get_convert_flags();
   11122              : 
   11123              :    /* only send notification once each 500ms */
   11124            0 :    if (now - last_time < 500)
   11125            0 :       return DB_SUCCESS;
   11126              : 
   11127            0 :    last_time = now;
   11128              : 
   11129              :    char buffer[32];
   11130            0 :    NET_COMMAND *nc = (NET_COMMAND *) buffer;
   11131              : 
   11132            0 :    nc->header.routine_id = MSG_BM;
   11133            0 :    nc->header.param_size = 0;
   11134              : 
   11135            0 :    if (convert_flags) {
   11136            0 :       rpc_convert_single(&nc->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
   11137            0 :       rpc_convert_single(&nc->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
   11138              :    }
   11139              : 
   11140              :    //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
   11141              : 
   11142              :    /* send the update notification to the client */
   11143            0 :    send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
   11144              : 
   11145            0 :    return BM_SUCCESS;
   11146              : }
   11147              : 
   11148              : /********************************************************************/
   11149            0 : INT bm_poll_event()
   11150              : /********************************************************************\
   11151              : 
   11152              :   Routine: bm_poll_event
   11153              : 
   11154              :   Purpose: Poll an event from a remote server. Gets called by
   11155              :            rpc_client_dispatch() and by cm_yield()
   11156              : 
   11157              :   Function value:
   11158              :     BM_SUCCESS       At least one event was received and dispatched
   11159              :     BM_ASYNC_RETURN  No events received
   11160              :     SS_ABORT         Network connection broken
   11161              : 
   11162              : \********************************************************************/
   11163              : {
   11164            0 :    BOOL dispatched_something = FALSE;
   11165              : 
   11166              :    //printf("bm_poll_event!\n");
   11167              : 
   11168            0 :    DWORD start_time = ss_millitime();
   11169              : 
   11170            0 :    std::vector<char> vec;
   11171              : 
   11172              :    /* loop over all requests */
   11173            0 :    _request_list_mutex.lock();
   11174            0 :    bool locked = true;
   11175            0 :    size_t n = _request_list.size();
   11176            0 :    for (size_t i = 0; i < n; i++) {
   11177            0 :       if (!locked) {
   11178            0 :          _request_list_mutex.lock();
   11179            0 :          locked = true;
   11180              :       }
   11181              :       /* continue if no dispatcher set (manual bm_receive_event) */
   11182            0 :       if (_request_list[i].dispatcher == NULL)
   11183            0 :          continue;
   11184              : 
   11185            0 :       int buffer_handle = _request_list[i].buffer_handle;
   11186              : 
   11187              :       /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
   11188            0 :       _request_list_mutex.unlock();
   11189            0 :       locked = false;
   11190              : 
   11191              :       do {
   11192              :          /* receive event */
   11193            0 :          int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
   11194              : 
   11195              :          //printf("bm_poll_event: request_id %d, buffer_handle %d, bm_receive_event(BM_NO_WAIT) status %d, vec size %d, capacity %d\n", request_id, buffer_handle, status, (int)vec.size(), (int)vec.capacity());
   11196              : 
   11197              :          /* call user function if successful */
   11198            0 :          if (status == BM_SUCCESS) {
   11199            0 :             bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
   11200            0 :             dispatched_something = TRUE;
   11201              :          }
   11202              : 
   11203              :          /* break if no more events */
   11204            0 :          if (status == BM_ASYNC_RETURN)
   11205            0 :             break;
   11206              : 
   11207              :          /* break if corrupted event buffer */
   11208            0 :          if (status == BM_TRUNCATED) {
   11209            0 :             cm_msg(MERROR, "bm_poll_event", "received event was truncated, buffer size %d is too small, see messages and increase /Experiment/MAX_EVENT_SIZE in ODB", (int)vec.size());
   11210              :          }
   11211              : 
   11212              :          /* break if corrupted event buffer */
   11213            0 :          if (status == BM_CORRUPTED)
   11214            0 :             return SS_ABORT;
   11215              : 
   11216              :          /* break if server died */
   11217            0 :          if (status == RPC_NET_ERROR) {
   11218            0 :             return SS_ABORT;
   11219              :          }
   11220              : 
   11221              :          /* stop after one second */
   11222            0 :          if (ss_millitime() - start_time > 1000) {
   11223            0 :             break;
   11224              :          }
   11225              : 
   11226            0 :       } while (TRUE);
   11227              :    }
   11228              : 
   11229            0 :    if (locked)
   11230            0 :       _request_list_mutex.unlock();
   11231              : 
   11232            0 :    if (dispatched_something)
   11233            0 :       return BM_SUCCESS;
   11234              :    else
   11235            0 :       return BM_ASYNC_RETURN;
   11236            0 : }
   11237              : 
   11238              : /********************************************************************/
   11239              : /**
   11240              : Clears event buffer and cache.
   11241              : If an event buffer is large and a consumer is slow in analyzing
   11242              : events, events are usually received some time after they are produced.
   11243              : This effect is even more experienced if a read cache is used
   11244              : (via bm_set_cache_size()).
   11245              : When changes to the hardware are made in the experience, the consumer will then
   11246              : still analyze old events before any new event which reflects the hardware change.
   11247              : Users can be fooled by looking at histograms which reflect the hardware change
   11248              : many seconds after they have been made.
   11249              : 
   11250              : To overcome this potential problem, the analyzer can call
   11251              : bm_empty_buffers() just after the hardware change has been made which
   11252              : skips all old events contained in event buffers and read caches.
   11253              : Technically this is done by forwarding the read pointer of the client.
   11254              : No events are really deleted, they are still visible to other clients like
   11255              : the logger.
   11256              : 
   11257              : Note that the front-end also contains write buffers which can delay the
   11258              : delivery of events.
   11259              : The standard front-end framework mfe.c reduces this effect by flushing
   11260              : all buffers once every second.
   11261              : @return BM_SUCCESS
   11262              : */
   11263            0 : INT bm_empty_buffers() {
   11264            0 :    if (rpc_is_remote())
   11265            0 :       return rpc_call(RPC_BM_EMPTY_BUFFERS);
   11266              : 
   11267              : #ifdef LOCAL_ROUTINES
   11268              :    {
   11269            0 :       std::vector<BUFFER*> mybuffers;
   11270              :       
   11271            0 :       gBuffersMutex.lock();
   11272            0 :       mybuffers = gBuffers;
   11273            0 :       gBuffersMutex.unlock();
   11274              : 
   11275              :       /* go through all buffers */
   11276            0 :       for (BUFFER* pbuf : mybuffers) {
   11277            0 :          if (!pbuf)
   11278            0 :             continue;
   11279            0 :          if (!pbuf->attached)
   11280            0 :             continue;
   11281              : 
   11282            0 :          int status = bm_skip_event(pbuf);
   11283            0 :          if (status != BM_SUCCESS)
   11284            0 :             return status;
   11285              :       }
   11286            0 :    }
   11287              : #endif                          /* LOCAL_ROUTINES */
   11288              : 
   11289            0 :    return BM_SUCCESS;
   11290              : }
   11291              : 
   11292              : /**dox***************************************************************/
   11293              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   11294              : 
   11295              : #define MAX_DEFRAG_EVENTS 10
   11296              : 
   11297              : typedef struct {
   11298              :    WORD event_id;
   11299              :    DWORD data_size;
   11300              :    DWORD received;
   11301              :    EVENT_HEADER *pevent;
   11302              : } EVENT_DEFRAG_BUFFER;
   11303              : 
   11304              : static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
   11305              : 
   11306              : /********************************************************************/
   11307            0 : static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
   11308              :                                 EVENT_HEADER *pevent, void *pdata,
   11309              :                                 EVENT_HANDLER *dispatcher)
   11310              : /********************************************************************\
   11311              : 
   11312              :   Routine: bm_defragment_event
   11313              : 
   11314              :   Purpose: Called internally from the event receiving routines
   11315              :            bm_push_event and bm_poll_event to recombine event
   11316              :            fragments and call the user callback routine upon
   11317              :            completion.
   11318              : 
   11319              :   Input:
   11320              :     HNDLE buffer_handle  Handle for the buffer containing event
   11321              :     HNDLE request_id     Handle for event request
   11322              :     EVENT_HEADER *pevent Pointer to event header
   11323              :     void *pata           Pointer to event data
   11324              :     dispatcher()         User callback routine
   11325              : 
   11326              :   Output:
   11327              :     <calls dispatcher() after successfull recombination of event>
   11328              : 
   11329              :   Function value:
   11330              :     void
   11331              : 
   11332              : \********************************************************************/
   11333              : {
   11334              :    INT i;
   11335              : 
   11336            0 :    if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
   11337              :       /*---- start new event ----*/
   11338              : 
   11339              :       //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
   11340              : 
   11341              :       /* check if fragments already stored */
   11342            0 :       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
   11343            0 :          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
   11344            0 :             break;
   11345              : 
   11346            0 :       if (i < MAX_DEFRAG_EVENTS) {
   11347            0 :          free(defrag_buffer[i].pevent);
   11348            0 :          defrag_buffer[i].pevent = NULL;
   11349            0 :          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
   11350            0 :          cm_msg(MERROR, "bm_defragement_event",
   11351              :                 "Received new event with ID %d while old fragments were not completed",
   11352            0 :                 (pevent->event_id & 0x0FFF));
   11353              :       }
   11354              : 
   11355              :       /* search new slot */
   11356            0 :       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
   11357            0 :          if (defrag_buffer[i].event_id == 0)
   11358            0 :             break;
   11359              : 
   11360            0 :       if (i == MAX_DEFRAG_EVENTS) {
   11361            0 :          cm_msg(MERROR, "bm_defragment_event",
   11362              :                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
   11363            0 :          return;
   11364              :       }
   11365              : 
   11366              :       /* check event size */
   11367            0 :       if (pevent->data_size != sizeof(DWORD)) {
   11368            0 :          cm_msg(MERROR, "bm_defragment_event",
   11369              :                 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
   11370              :                 pevent->data_size, (int) sizeof(DWORD));
   11371            0 :          return;
   11372              :       }
   11373              : 
   11374              :       /* setup defragment buffer */
   11375            0 :       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
   11376            0 :       defrag_buffer[i].data_size = *(DWORD *) pdata;
   11377            0 :       defrag_buffer[i].received = 0;
   11378            0 :       defrag_buffer[i].pevent = (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
   11379              : 
   11380            0 :       if (defrag_buffer[i].pevent == NULL) {
   11381            0 :          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
   11382            0 :          cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
   11383            0 :          return;
   11384              :       }
   11385              : 
   11386            0 :       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
   11387            0 :       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
   11388            0 :       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
   11389              : 
   11390              :       // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
   11391              :       //       pevent->serial_number, defrag_buffer[i].data_size);
   11392              : 
   11393            0 :       return;
   11394              :    }
   11395              : 
   11396              :    /* search buffer for that event */
   11397            0 :    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
   11398            0 :       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
   11399            0 :          break;
   11400              : 
   11401            0 :    if (i == MAX_DEFRAG_EVENTS) {
   11402              :       /* no buffer available -> no first fragment received */
   11403            0 :       cm_msg(MERROR, "bm_defragement_event",
   11404              :              "Received fragment without first fragment (ID %d) Ser#:%d",
   11405            0 :              pevent->event_id & 0x0FFF, pevent->serial_number);
   11406            0 :       return;
   11407              :    }
   11408              : 
   11409              :    /* add fragment to buffer */
   11410            0 :    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
   11411            0 :       free(defrag_buffer[i].pevent);
   11412            0 :       defrag_buffer[i].pevent = NULL;
   11413            0 :       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
   11414            0 :       cm_msg(MERROR, "bm_defragement_event",
   11415              :              "Received fragments with more data (%d) than event size (%d)",
   11416            0 :              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
   11417            0 :       return;
   11418              :    }
   11419              : 
   11420            0 :    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
   11421            0 :           defrag_buffer[i].received, pdata, pevent->data_size);
   11422              : 
   11423            0 :    defrag_buffer[i].received += pevent->data_size;
   11424              : 
   11425              :    //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
   11426              :    //       defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
   11427              : 
   11428            0 :    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
   11429              :       /* event complete */
   11430            0 :       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
   11431            0 :       free(defrag_buffer[i].pevent);
   11432            0 :       defrag_buffer[i].pevent = NULL;
   11433            0 :       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
   11434              :    }
   11435              : }
   11436              : 
   11437              : /**dox***************************************************************/
   11438              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   11439              : 
   11440              : /**dox***************************************************************/
   11441              : /** @} *//* end of bmfunctionc */
   11442              : 
   11443              : /**dox***************************************************************/
   11444              : /** @addtogroup rpc_xxx
   11445              :  *
   11446              :  *  @{  */
   11447              : 
   11448              : /********************************************************************\
   11449              : *                                                                    *
   11450              : *                         RPC functions                              *
   11451              : *                                                                    *
   11452              : \********************************************************************/
   11453              : 
   11454              : class RPC_CLIENT_CONNECTION
   11455              : {
   11456              : public:
   11457              :    std::atomic_bool connected{false}; /*  socket is connected */
   11458              :    std::string host_name;       /*  server name             */
   11459              :    int port = 0;                /*  server port             */
   11460              :    std::mutex mutex;            /*  connection lock           */
   11461              :    int index = 0;               /* index in the connection array */
   11462              :    std::string client_name;     /* name of remote client    */
   11463              :    int send_sock = 0;           /*  tcp socket              */
   11464              :    int remote_hw_type = 0;      /*  remote hardware type    */
   11465              :    int rpc_timeout = 0;         /*  timeout in milliseconds */
   11466              : 
   11467            0 :    void print() {
   11468            0 :       printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
   11469              :              index,
   11470              :              client_name.c_str(),
   11471              :              host_name.c_str(),
   11472              :              port,
   11473              :              send_sock,
   11474            0 :              int(connected),
   11475              :              rpc_timeout);
   11476            0 :    }
   11477              : 
   11478            0 :    void close_locked() {
   11479            0 :       if (send_sock > 0) {
   11480            0 :          ss_socket_close(&send_sock);
   11481              :       }
   11482            0 :       connected = false;
   11483            0 :    }
   11484              : };
   11485              : 
   11486              : /* globals */
   11487              : 
   11488              : //
   11489              : // locking rules for client connections:
   11490              : //
   11491              : // lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
   11492              : // lock _client_connections_mutex, look at _client_connections vector and c->connected, lock individual connection, recheck c->connected, work on the connection, unlock the connection, unlock _client_connections_mutex
   11493              : // lock individual connection, check c->connected, work on the connection, unlock connection
   11494              : //
   11495              : // ok to access without locking client connection:
   11496              : //
   11497              : // - c->connected (std::atomic, but must recheck it after taking the lock)
   11498              : // - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
   11499              : //
   11500              : // this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
   11501              : // this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
   11502              : //
   11503              : // lifetime of client connections:
   11504              : //
   11505              : // - client connection slots are allocated by rpc_client_connect()
   11506              : // - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
   11507              : // - client slots marked NULL are free and will be reused by rpc_client_connect()
   11508              : // - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
   11509              : // - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
   11510              : // - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
   11511              : // - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
   11512              : //   client call will see an empty slot and return an error
   11513              : // - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
   11514              : //   client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
   11515              : //   only when one is sure no other threads are running concurrent rpc client calls.
   11516              : //
   11517              : 
   11518              : static std::mutex _client_connections_mutex;
   11519              : static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
   11520              : 
   11521              : static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
   11522              : static bool _rpc_is_remote = false;
   11523              :    
   11524              : //static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
   11525              : static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
   11526              : static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
   11527              : 
   11528            0 : static RPC_SERVER_ACCEPTION* rpc_get_server_acception(int idx)
   11529              : {
   11530            0 :    assert(idx >= 0);
   11531            0 :    assert(idx < (int)_server_acceptions.size());
   11532            0 :    assert(_server_acceptions[idx] != NULL);
   11533            0 :    return _server_acceptions[idx];
   11534              : }
   11535              : 
   11536            0 : RPC_SERVER_ACCEPTION* rpc_get_mserver_acception()
   11537              : {
   11538            0 :    return _mserver_acception;
   11539              : }
   11540              : 
   11541            0 : static RPC_SERVER_ACCEPTION* rpc_new_server_acception()
   11542              : {
   11543            0 :    for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
   11544            0 :       if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
   11545              :          //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
   11546            0 :          return _server_acceptions[idx];
   11547              :       }
   11548              :    }
   11549              : 
   11550            0 :    RPC_SERVER_ACCEPTION* sa = new RPC_SERVER_ACCEPTION;
   11551              : 
   11552            0 :    for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
   11553            0 :       if (_server_acceptions[idx] == NULL) {
   11554              :          //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
   11555            0 :          _server_acceptions[idx] = sa;
   11556            0 :          return _server_acceptions[idx];
   11557              :       }
   11558              :    }
   11559              : 
   11560              :    //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
   11561            0 :    _server_acceptions.push_back(sa);
   11562              :    
   11563            0 :    return sa;
   11564              : }
   11565              : 
   11566            0 : void RPC_SERVER_ACCEPTION::close()
   11567              : {
   11568              :    //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
   11569              : 
   11570            0 :    if (is_mserver) {
   11571            0 :       assert(_mserver_acception == this);
   11572            0 :       _mserver_acception = NULL;
   11573            0 :       is_mserver = false;
   11574              :    }
   11575              :    
   11576              :    /* close server connection */
   11577            0 :    if (recv_sock)
   11578            0 :       ss_socket_close(&recv_sock);
   11579            0 :    if (send_sock)
   11580            0 :       ss_socket_close(&send_sock);
   11581            0 :    if (event_sock)
   11582            0 :       ss_socket_close(&event_sock);
   11583              : 
   11584              :    /* free TCP cache */
   11585            0 :    if (net_buffer) {
   11586              :       //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
   11587            0 :       free(net_buffer);
   11588            0 :       net_buffer = NULL;
   11589            0 :       net_buffer_size = 0;
   11590              :    }
   11591              : 
   11592              :    /* mark this entry as invalid */
   11593            0 :    clear();
   11594            0 : }
   11595              : 
   11596              : static std::vector<RPC_LIST> rpc_list;
   11597              : static std::mutex rpc_list_mutex;
   11598              : 
   11599              : static int _opt_tcp_size = OPT_TCP_SIZE;
   11600              : 
   11601              : 
   11602              : /********************************************************************\
   11603              : *                       conversion functions                         *
   11604              : \********************************************************************/
   11605              : 
   11606            0 : void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
   11607            0 :    *convert_flags = 0;
   11608              : 
   11609              :    /* big/little endian conversion */
   11610            0 :    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
   11611            0 :         (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
   11612            0 :                                            && (hw_type & DRI_BIG_ENDIAN)))
   11613            0 :       *convert_flags |= CF_ENDIAN;
   11614              : 
   11615              :    /* float conversion between IEEE and VAX G */
   11616            0 :    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
   11617            0 :       *convert_flags |= CF_VAX2IEEE;
   11618              : 
   11619              :    /* float conversion between VAX G and IEEE */
   11620            0 :    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
   11621            0 :       *convert_flags |= CF_IEEE2VAX;
   11622              : 
   11623              :    ///* ASCII format */
   11624              :    //if (remote_hw_type & DR_ASCII)
   11625              :    //   *convert_flags |= CF_ASCII;
   11626            0 : }
   11627              : 
   11628              : /********************************************************************/
   11629            0 : void rpc_get_convert_flags(INT *convert_flags) {
   11630            0 :    rpc_calc_convert_flags(rpc_get_hw_type(), _server_connection.remote_hw_type, convert_flags);
   11631            0 : }
   11632              : 
   11633              : /********************************************************************/
   11634            0 : void rpc_ieee2vax_float(float *var) {
   11635              :    unsigned short int lo, hi;
   11636              : 
   11637              :    /* swap hi and lo word */
   11638            0 :    lo = *((short int *) (var) + 1);
   11639            0 :    hi = *((short int *) (var));
   11640              : 
   11641              :    /* correct exponent */
   11642            0 :    if (lo != 0)
   11643            0 :       lo += 0x100;
   11644              : 
   11645            0 :    *((short int *) (var) + 1) = hi;
   11646            0 :    *((short int *) (var)) = lo;
   11647            0 : }
   11648              : 
   11649            0 : void rpc_vax2ieee_float(float *var) {
   11650              :    unsigned short int lo, hi;
   11651              : 
   11652              :    /* swap hi and lo word */
   11653            0 :    lo = *((short int *) (var) + 1);
   11654            0 :    hi = *((short int *) (var));
   11655              : 
   11656              :    /* correct exponent */
   11657            0 :    if (hi != 0)
   11658            0 :       hi -= 0x100;
   11659              : 
   11660            0 :    *((short int *) (var) + 1) = hi;
   11661            0 :    *((short int *) (var)) = lo;
   11662              : 
   11663            0 : }
   11664              : 
   11665            0 : void rpc_vax2ieee_double(double *var) {
   11666              :    unsigned short int i1, i2, i3, i4;
   11667              : 
   11668              :    /* swap words */
   11669            0 :    i1 = *((short int *) (var) + 3);
   11670            0 :    i2 = *((short int *) (var) + 2);
   11671            0 :    i3 = *((short int *) (var) + 1);
   11672            0 :    i4 = *((short int *) (var));
   11673              : 
   11674              :    /* correct exponent */
   11675            0 :    if (i4 != 0)
   11676            0 :       i4 -= 0x20;
   11677              : 
   11678            0 :    *((short int *) (var) + 3) = i4;
   11679            0 :    *((short int *) (var) + 2) = i3;
   11680            0 :    *((short int *) (var) + 1) = i2;
   11681            0 :    *((short int *) (var)) = i1;
   11682            0 : }
   11683              : 
   11684            0 : void rpc_ieee2vax_double(double *var) {
   11685              :    unsigned short int i1, i2, i3, i4;
   11686              : 
   11687              :    /* swap words */
   11688            0 :    i1 = *((short int *) (var) + 3);
   11689            0 :    i2 = *((short int *) (var) + 2);
   11690            0 :    i3 = *((short int *) (var) + 1);
   11691            0 :    i4 = *((short int *) (var));
   11692              : 
   11693              :    /* correct exponent */
   11694            0 :    if (i1 != 0)
   11695            0 :       i1 += 0x20;
   11696              : 
   11697            0 :    *((short int *) (var) + 3) = i4;
   11698            0 :    *((short int *) (var) + 2) = i3;
   11699            0 :    *((short int *) (var) + 1) = i2;
   11700            0 :    *((short int *) (var)) = i1;
   11701            0 : }
   11702              : 
   11703              : /********************************************************************/
   11704            0 : void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
   11705              : 
   11706            0 :    if (convert_flags & CF_ENDIAN) {
   11707            0 :       if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
   11708            0 :       if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
   11709            0 :       if (tid == TID_DOUBLE) QWORD_SWAP(data);
   11710              :    }
   11711              : 
   11712            0 :    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
   11713            0 :        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
   11714            0 :       if (tid == TID_FLOAT)
   11715            0 :          rpc_ieee2vax_float((float *) data);
   11716            0 :       if (tid == TID_DOUBLE)
   11717            0 :          rpc_ieee2vax_double((double *) data);
   11718              :    }
   11719              : 
   11720            0 :    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
   11721            0 :        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
   11722            0 :       if (tid == TID_FLOAT)
   11723            0 :          rpc_vax2ieee_float((float *) data);
   11724            0 :       if (tid == TID_DOUBLE)
   11725            0 :          rpc_vax2ieee_double((double *) data);
   11726              :    }
   11727            0 : }
   11728              : 
   11729            0 : void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
   11730              : /********************************************************************\
   11731              : 
   11732              :   Routine: rpc_convert_data
   11733              : 
   11734              :   Purpose: Convert data format between differenct computers
   11735              : 
   11736              :   Input:
   11737              :     void   *data            Pointer to data
   11738              :     INT    tid              Type ID of data, one of TID_xxx
   11739              :     INT    flags            Combination of following flags:
   11740              :                               RPC_IN: data is input parameter
   11741              :                               RPC_OUT: data is output variable
   11742              :                               RPC_FIXARRAY, RPC_VARARRAY: data is array
   11743              :                                 of "size" bytes (see next param.)
   11744              :                               RPC_OUTGOING: data is outgoing
   11745              :     INT    total_size       Size of bytes of data. Used for variable
   11746              :                             length arrays.
   11747              :     INT    convert_flags    Flags for data conversion
   11748              : 
   11749              :   Output:
   11750              :     void   *data            Is converted according to _convert_flag
   11751              :                             value
   11752              : 
   11753              :   Function value:
   11754              :     RPC_SUCCESS             Successful completion
   11755              : 
   11756              : \********************************************************************/
   11757              : {
   11758              :    /* convert array */
   11759            0 :    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
   11760            0 :       int single_size = rpc_tid_size(tid);
   11761              :       /* don't convert TID_ARRAY & TID_STRUCT */
   11762            0 :       if (single_size == 0)
   11763            0 :          return;
   11764              : 
   11765            0 :       int n = total_size / single_size;
   11766              : 
   11767            0 :       for (int i = 0; i < n; i++) {
   11768            0 :          char* p = (char *) data + (i * single_size);
   11769            0 :          rpc_convert_single(p, tid, flags, convert_flags);
   11770              :       }
   11771              :    } else {
   11772            0 :       rpc_convert_single(data, tid, flags, convert_flags);
   11773              :    }
   11774              : }
   11775              : 
   11776              : /********************************************************************\
   11777              : *                       type ID functions                            *
   11778              : \********************************************************************/
   11779              : 
   11780          515 : INT rpc_tid_size(INT id) {
   11781          515 :    if (id >= 0 && id < TID_LAST)
   11782          515 :       return tid_size[id];
   11783              : 
   11784            0 :    return 0;
   11785              : }
   11786              : 
   11787          442 : const char *rpc_tid_name(INT id) {
   11788          442 :    if (id >= 0 && id < TID_LAST)
   11789          442 :       return tid_name[id];
   11790              :    else
   11791            0 :       return "<unknown>";
   11792              : }
   11793              : 
   11794           44 : const char *rpc_tid_name_old(INT id) {
   11795           44 :    if (id >= 0 && id < TID_LAST)
   11796           44 :       return tid_name_old[id];
   11797              :    else
   11798            0 :       return "<unknown>";
   11799              : }
   11800              : 
   11801            0 : int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
   11802              : {
   11803            0 :    for (int i=0; i<TID_LAST; i++) {
   11804            0 :       if (strcmp(name, tid_name[i]) == 0)
   11805            0 :          return i;
   11806              :    }
   11807              : 
   11808            0 :    for (int i=0; i<TID_LAST; i++) {
   11809            0 :       if (strcmp(name, tid_name_old[i]) == 0)
   11810            0 :          return i;
   11811              :    }
   11812              : 
   11813            0 :    return 0;
   11814              : }
   11815              : 
   11816              : /********************************************************************\
   11817              : *                        client functions                            *
   11818              : \********************************************************************/
   11819              : 
   11820              : /********************************************************************/
   11821              : /**
   11822              : Register RPC client for standalone mode (without standard
   11823              :            midas server)
   11824              : @param list           Array of RPC_LIST structures containing
   11825              :                             function IDs and parameter definitions.
   11826              :                             The end of the list must be indicated by
   11827              :                             a function ID of zero.
   11828              : @param name          Name of this client
   11829              : @return RPC_SUCCESS
   11830              : */
   11831            0 : INT rpc_register_client(const char *name, RPC_LIST *list) {
   11832            0 :    rpc_set_name(name);
   11833            0 :    rpc_register_functions(rpc_get_internal_list(0), NULL);
   11834            0 :    rpc_register_functions(list, NULL);
   11835              : 
   11836            0 :    return RPC_SUCCESS;
   11837              : }
   11838              : 
   11839              : /********************************************************************/
   11840              : /**
   11841              : Register a set of RPC functions (both as clients or servers)
   11842              : @param new_list       Array of RPC_LIST structures containing
   11843              :                             function IDs and parameter definitions.
   11844              :                             The end of the list must be indicated by
   11845              :                             a function ID of zero.
   11846              : @param func          Default dispatch function
   11847              : 
   11848              : @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
   11849              : */
   11850            4 : INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
   11851              : {
   11852          184 :    for (int i = 0; new_list[i].id != 0; i++) {
   11853              :       /* check valid ID for user functions */
   11854          180 :       if (new_list != rpc_get_internal_list(0) &&
   11855          180 :           new_list != rpc_get_internal_list(1) && (new_list[i].id < RPC_MIN_ID
   11856            0 :                                                    || new_list[i].id > RPC_MAX_ID)) {
   11857            0 :          cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
   11858              :       }
   11859              :    }
   11860              : 
   11861            4 :    std::lock_guard<std::mutex> guard(rpc_list_mutex);
   11862              : 
   11863              :    /* check double defined functions */
   11864          184 :    for (int i = 0; new_list[i].id != 0; i++) {
   11865          702 :       for (size_t j = 0; j < rpc_list.size(); j++) {
   11866          522 :          if (rpc_list[j].id == new_list[i].id) {
   11867            0 :             return RPC_DOUBLE_DEFINED;
   11868              :          }
   11869              :       }
   11870              :    }
   11871              : 
   11872              :    /* append new functions */
   11873          184 :    for (int i = 0; new_list[i].id != 0; i++) {
   11874          180 :       RPC_LIST e = new_list[i];
   11875              : 
   11876              :       /* set default dispatcher */
   11877          180 :       if (e.dispatch == NULL) {
   11878          180 :          e.dispatch = func;
   11879              :       }
   11880              : 
   11881          180 :       rpc_list.push_back(e);
   11882              :    }
   11883              : 
   11884            4 :    return RPC_SUCCESS;
   11885            4 : }
   11886              : 
   11887              : 
   11888              : 
   11889              : /**dox***************************************************************/
   11890              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   11891              : 
   11892              : /********************************************************************/
   11893            2 : INT rpc_deregister_functions()
   11894              : /********************************************************************\
   11895              : 
   11896              :   Routine: rpc_deregister_functions
   11897              : 
   11898              :   Purpose: Free memory of previously registered functions
   11899              : 
   11900              :   Input:
   11901              :     none
   11902              : 
   11903              :   Output:
   11904              :     none
   11905              : 
   11906              :   Function value:
   11907              :     RPC_SUCCESS              Successful completion
   11908              : 
   11909              : \********************************************************************/
   11910              : {
   11911            2 :    rpc_list_mutex.lock();
   11912            2 :    rpc_list.clear();
   11913            2 :    rpc_list_mutex.unlock();
   11914              : 
   11915            2 :    return RPC_SUCCESS;
   11916              : }
   11917              : 
   11918              : 
   11919              : /********************************************************************/
   11920            7 : INT rpc_register_function(INT id, INT(*func)(INT, void **))
   11921              : /********************************************************************\
   11922              : 
   11923              :   Routine: rpc_register_function
   11924              : 
   11925              :   Purpose: Replace a dispatch function for a specific rpc routine
   11926              : 
   11927              :   Input:
   11928              :     INT      id             RPC ID
   11929              :     INT      *func          New dispatch function
   11930              : 
   11931              :   Output:
   11932              :    <implicit: func gets copied to rpc_list>
   11933              : 
   11934              :   Function value:
   11935              :    RPC_SUCCESS              Successful completion
   11936              :    RPC_INVALID_ID           RPC ID not found
   11937              : 
   11938              : \********************************************************************/
   11939              : {
   11940            7 :    std::lock_guard<std::mutex> guard(rpc_list_mutex);
   11941              : 
   11942          584 :    for (size_t i = 0; i < rpc_list.size(); i++) {
   11943          584 :       if (rpc_list[i].id == id) {
   11944            7 :          rpc_list[i].dispatch = func;
   11945            7 :          return RPC_SUCCESS;
   11946              :       }
   11947              :    }
   11948              : 
   11949            0 :    return RPC_INVALID_ID;
   11950            7 : }
   11951              : 
   11952              : /********************************************************************/
   11953              : 
   11954            0 : static int handle_msg_odb(int n, const NET_COMMAND *nc) {
   11955              :    //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
   11956            0 :    if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
   11957              :       /* update a changed record */
   11958            0 :       HNDLE hDB = *((INT *) nc->param);
   11959            0 :       HNDLE hKeyRoot = *((INT *) nc->param + 1);
   11960            0 :       HNDLE hKey = *((INT *) nc->param + 2);
   11961            0 :       int index = *((INT *) nc->param + 3);
   11962            0 :       return db_update_record_local(hDB, hKeyRoot, hKey, index);
   11963              :    }
   11964            0 :    return CM_VERSION_MISMATCH;
   11965              : }
   11966              : 
   11967              : /********************************************************************/
   11968            0 : INT rpc_client_dispatch(int sock)
   11969              : /********************************************************************\
   11970              : 
   11971              :   Routine: rpc_client_dispatch
   11972              : 
   11973              :   Purpose: Receive data from the mserver: watchdog and buffer notification messages
   11974              : 
   11975              : \********************************************************************/
   11976              : {
   11977            0 :    INT status = 0;
   11978              :    char net_buffer[256];
   11979              : 
   11980            0 :    int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
   11981            0 :    if (n <= 0)
   11982            0 :       return SS_ABORT;
   11983              : 
   11984            0 :    NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
   11985              : 
   11986            0 :    if (nc->header.routine_id == MSG_ODB) {
   11987            0 :       status = handle_msg_odb(n, nc);
   11988            0 :    } else if (nc->header.routine_id == MSG_WATCHDOG) {
   11989            0 :       nc->header.routine_id = 1;
   11990            0 :       nc->header.param_size = 0;
   11991            0 :       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
   11992            0 :       status = RPC_SUCCESS;
   11993            0 :    } else if (nc->header.routine_id == MSG_BM) {
   11994              :       fd_set readfds;
   11995              :       struct timeval timeout;
   11996              : 
   11997              :       //printf("rpc_client_dispatch: received MSG_BM!\n");
   11998              : 
   11999              :       /* receive further messages to empty TCP queue */
   12000              :       do {
   12001            0 :          FD_ZERO(&readfds);
   12002            0 :          FD_SET(sock, &readfds);
   12003              : 
   12004            0 :          timeout.tv_sec = 0;
   12005            0 :          timeout.tv_usec = 0;
   12006              : 
   12007            0 :          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
   12008              : 
   12009            0 :          if (FD_ISSET(sock, &readfds)) {
   12010            0 :             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
   12011            0 :             if (n <= 0)
   12012            0 :                return SS_ABORT;
   12013              : 
   12014            0 :             if (nc->header.routine_id == MSG_ODB) {
   12015            0 :                status = handle_msg_odb(n, nc);
   12016            0 :             } else if (nc->header.routine_id == MSG_WATCHDOG) {
   12017            0 :                nc->header.routine_id = 1;
   12018            0 :                nc->header.param_size = 0;
   12019            0 :                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
   12020            0 :                status = RPC_SUCCESS;
   12021              :             }
   12022              :          }
   12023              : 
   12024            0 :       } while (FD_ISSET(sock, &readfds));
   12025              : 
   12026              :       /* poll event from server */
   12027            0 :       status = bm_poll_event();
   12028              :    }
   12029              : 
   12030            0 :    return status;
   12031              : }
   12032              : 
   12033              : 
   12034              : /********************************************************************/
   12035            0 : INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
   12036              : /********************************************************************\
   12037              : 
   12038              :   Routine: rpc_client_connect
   12039              : 
   12040              :   Purpose: Establish a network connection to a remote client
   12041              : 
   12042              :   Input:
   12043              :     char *host_name          IP address of host to connect to.
   12044              :     INT  port                TPC port to connect to.
   12045              :     char *clinet_name        Client program name
   12046              : 
   12047              :   Output:
   12048              :     HNDLE *hConnection       Handle for new connection which can be used
   12049              :                              in future rpc_call(hConnection....) calls
   12050              : 
   12051              :   Function value:
   12052              :     RPC_SUCCESS              Successful completion
   12053              :     RPC_NET_ERROR            Error in socket call
   12054              :     RPC_NO_CONNECTION        Maximum number of connections reached
   12055              :     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
   12056              : 
   12057              : \********************************************************************/
   12058              : {
   12059              :    INT i, status;
   12060            0 :    bool debug = false;
   12061              : 
   12062              :    /* check if cm_connect_experiment was called */
   12063            0 :    if (_client_name.length() == 0) {
   12064            0 :       cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
   12065            0 :       return RPC_NOT_REGISTERED;
   12066              :    }
   12067              : 
   12068              :    /* refuse connection to port 0 */
   12069            0 :    if (port == 0) {
   12070            0 :       cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
   12071            0 :       return RPC_NET_ERROR;
   12072              :    }
   12073              : 
   12074            0 :    RPC_CLIENT_CONNECTION* c = NULL;
   12075              : 
   12076              :    static std::mutex gHostnameMutex;
   12077              : 
   12078              :    {
   12079            0 :       std::lock_guard<std::mutex> guard(_client_connections_mutex);
   12080              : 
   12081            0 :       if (debug) {
   12082            0 :          printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
   12083            0 :          for (size_t i = 0; i < _client_connections.size(); i++) {
   12084            0 :             if (_client_connections[i]) {
   12085            0 :                printf("client connection %d: ", (int)i);
   12086            0 :                _client_connections[i]->print();
   12087            0 :                printf("\n");
   12088              :             }
   12089              :          }
   12090              :       }
   12091              : 
   12092              :       // slot with index 0 is not used, fill it with a NULL
   12093              :       
   12094            0 :       if (_client_connections.empty()) {
   12095            0 :          _client_connections.push_back(NULL);
   12096              :       }
   12097              : 
   12098            0 :       bool hostname_locked = false;
   12099              : 
   12100              :       /* check if connection already exists */
   12101            0 :       for (size_t i = 1; i < _client_connections.size(); i++) {
   12102            0 :          RPC_CLIENT_CONNECTION* c = _client_connections[i];
   12103            0 :          if (c && c->connected) {
   12104              : 
   12105            0 :             if (!hostname_locked) {
   12106            0 :                gHostnameMutex.lock();
   12107            0 :                hostname_locked = true;
   12108              :             }
   12109              : 
   12110            0 :             if ((c->host_name == host_name) && (c->port == port)) {
   12111              :                // NB: we must release the hostname lock before taking
   12112              :                // c->mutex to avoid a locking order inversion deadlock:
   12113              :                // later on we lock the hostname mutex while holding the c->mutex
   12114            0 :                gHostnameMutex.unlock();
   12115            0 :                hostname_locked = false;
   12116            0 :                std::lock_guard<std::mutex> cguard(c->mutex);
   12117              :                // check if socket is still connected
   12118            0 :                if (c->connected) {
   12119              :                   // found connection slot with matching hostname and port number
   12120            0 :                   status = ss_socket_wait(c->send_sock, 0);
   12121            0 :                   if (status == SS_TIMEOUT) { // yes, still connected and empty
   12122              :                      // so reuse it connection
   12123            0 :                      *hConnection = c->index;
   12124            0 :                      if (debug) {
   12125            0 :                         printf("already connected: ");
   12126            0 :                         c->print();
   12127            0 :                         printf("\n");
   12128              :                      }
   12129              :                      // implicit unlock of c->mutex
   12130              :                      // gHostnameLock is not locked here
   12131            0 :                      return RPC_SUCCESS;
   12132              :                   }
   12133              :                   //cm_msg(MINFO, "rpc_client_connect", "Stale connection to \"%s\" on host %s is closed", _client_connection[i].client_name, _client_connection[i].host_name);
   12134            0 :                   c->close_locked();
   12135              :                }
   12136              :                // implicit unlock of c->mutex
   12137            0 :             }
   12138              :          }
   12139              :       }
   12140              : 
   12141            0 :       if (hostname_locked) {
   12142            0 :          gHostnameMutex.unlock();
   12143            0 :          hostname_locked = false;
   12144              :       }
   12145              :       
   12146              :       // only start reusing connections once we have
   12147              :       // a good number of slots allocated.
   12148            0 :       if (_client_connections.size() > 10) {
   12149              :          static int last_reused = 0;
   12150              : 
   12151            0 :          int size = _client_connections.size();
   12152            0 :          for (int j = 1; j < size; j++) {
   12153            0 :             int i = (last_reused + j) % size;
   12154            0 :             if (_client_connections[i] && !_client_connections[i]->connected) {
   12155            0 :                c = _client_connections[i];
   12156            0 :                if (debug) {
   12157            0 :                   printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
   12158            0 :                   c->print();
   12159            0 :                   printf("\n");
   12160              :                }
   12161            0 :                last_reused = i;
   12162            0 :                break;
   12163              :             }
   12164              :          }
   12165              :       }
   12166              : 
   12167              :       // no slots to reuse, allocate a new slot.
   12168            0 :       if (!c) {
   12169            0 :          c = new RPC_CLIENT_CONNECTION;
   12170              : 
   12171              :          // if empty slot not found, add to end of array
   12172            0 :          c->index = _client_connections.size();
   12173            0 :          _client_connections.push_back(c);
   12174              : 
   12175            0 :          if (debug) {
   12176            0 :             printf("new connection appended to array: ");
   12177            0 :             c->print();
   12178            0 :             printf("\n");
   12179              :          }
   12180              :       }
   12181              : 
   12182            0 :       c->mutex.lock();
   12183            0 :       c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
   12184              : 
   12185              :       // done with the array of connections
   12186              :       // implicit unlock of _client_connections_mutex
   12187            0 :    }
   12188              : 
   12189              :    // locked connection slot for new connection
   12190            0 :    assert(c != NULL);
   12191              : 
   12192            0 :    std::string errmsg;
   12193              :    
   12194              :    /* create a new socket for connecting to remote server */
   12195            0 :    status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
   12196            0 :    if (status != SS_SUCCESS) {
   12197            0 :       cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
   12198            0 :       c->mutex.unlock();
   12199            0 :       return RPC_NET_ERROR;
   12200              :    }
   12201              : 
   12202            0 :    gHostnameMutex.lock();
   12203              : 
   12204            0 :    c->host_name   = host_name;
   12205            0 :    c->port        = port;
   12206              : 
   12207            0 :    gHostnameMutex.unlock();
   12208              : 
   12209            0 :    c->client_name = client_name;
   12210            0 :    c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
   12211              : 
   12212              :    /* set TCP_NODELAY option for better performance */
   12213            0 :    i = 1;
   12214            0 :    setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
   12215              : 
   12216              :    /* send local computer info */
   12217            0 :    std::string local_prog_name = rpc_get_name();
   12218            0 :    std::string local_host_name = ss_gethostname();
   12219              : 
   12220            0 :    int hw_type = rpc_get_hw_type();
   12221              : 
   12222            0 :    std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
   12223              : 
   12224            0 :    int size = cstr.length() + 1;
   12225            0 :    i = send(c->send_sock, cstr.c_str(), size, 0);
   12226            0 :    if (i < 0 || i != size) {
   12227            0 :       cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
   12228            0 :       c->mutex.unlock();
   12229            0 :       return RPC_NET_ERROR;
   12230              :    }
   12231              : 
   12232            0 :    bool restore_watchdog_timeout = false;
   12233              :    BOOL watchdog_call;
   12234              :    DWORD watchdog_timeout;
   12235            0 :    cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
   12236              : 
   12237              :    //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
   12238              : 
   12239            0 :    if (_rpc_connect_timeout >= (int) watchdog_timeout) {
   12240            0 :       restore_watchdog_timeout = true;
   12241            0 :       cm_set_watchdog_params(watchdog_call, _rpc_connect_timeout + 1000);
   12242              :    }
   12243              : 
   12244              :    char str[256];
   12245              : 
   12246              :    /* receive remote computer info */
   12247            0 :    i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
   12248              : 
   12249            0 :    if (restore_watchdog_timeout) {
   12250            0 :       cm_set_watchdog_params(watchdog_call, watchdog_timeout);
   12251              :    }
   12252              : 
   12253            0 :    if (i <= 0) {
   12254            0 :       cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
   12255            0 :       c->close_locked();
   12256            0 :       c->mutex.unlock();
   12257            0 :       return RPC_NET_ERROR;
   12258              :    }
   12259              : 
   12260            0 :    int remote_hw_type = 0;
   12261              :    char remote_version[32];
   12262            0 :    remote_version[0] = 0;
   12263            0 :    sscanf(str, "%d %s", &remote_hw_type, remote_version);
   12264              : 
   12265            0 :    c->remote_hw_type = remote_hw_type;
   12266              : 
   12267              :    /* print warning if version patch level doesn't agree */
   12268              :    char v1[32];
   12269            0 :    mstrlcpy(v1, remote_version, sizeof(v1));
   12270            0 :    if (strchr(v1, '.'))
   12271            0 :       if (strchr(strchr(v1, '.') + 1, '.'))
   12272            0 :          *strchr(strchr(v1, '.') + 1, '.') = 0;
   12273              : 
   12274            0 :    mstrlcpy(str, cm_get_version(), sizeof(str));
   12275            0 :    if (strchr(str, '.'))
   12276            0 :       if (strchr(strchr(str, '.') + 1, '.'))
   12277            0 :          *strchr(strchr(str, '.') + 1, '.') = 0;
   12278              : 
   12279            0 :    if (strcmp(v1, str) != 0) {
   12280            0 :       cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
   12281              :    }
   12282              : 
   12283            0 :    c->connected = true;
   12284              : 
   12285            0 :    *hConnection = c->index;
   12286              : 
   12287            0 :    c->mutex.unlock();
   12288              : 
   12289            0 :    return RPC_SUCCESS;
   12290            0 : }
   12291              : 
   12292              : /********************************************************************/
   12293            0 : void rpc_client_check()
   12294              : /********************************************************************\
   12295              : 
   12296              :   Routine: rpc_client_check
   12297              : 
   12298              :   Purpose: Check all client connections if remote client closed link
   12299              : 
   12300              : \********************************************************************/
   12301              : {
   12302              : #if 0
   12303              :    for (i = 0; i < MAX_RPC_CONNECTION; i++)
   12304              :       if (_client_connection[i].send_sock != 0)
   12305              :          printf("slot %d, checking client %s socket %d, connected %d\n", i, _client_connection[i].client_name, _client_connection[i].send_sock, _client_connection[i].connected);
   12306              : #endif
   12307              : 
   12308            0 :    std::lock_guard<std::mutex> guard(_client_connections_mutex);
   12309              : 
   12310              :    /* check for broken connections */
   12311            0 :    for (unsigned i = 0; i < _client_connections.size(); i++) {
   12312            0 :       RPC_CLIENT_CONNECTION* c = _client_connections[i];
   12313            0 :       if (c && c->connected) {
   12314            0 :          std::lock_guard<std::mutex> cguard(c->mutex);
   12315              : 
   12316            0 :          if (!c->connected) {
   12317              :             // implicit unlock
   12318            0 :             continue;
   12319              :          }
   12320              : 
   12321              :          //printf("rpc_client_check: connection %d: ", i);
   12322              :          //c->print();
   12323              :          //printf("\n");
   12324              : 
   12325            0 :          int ok = 0;
   12326              : 
   12327              :          fd_set readfds;
   12328            0 :          FD_ZERO(&readfds);
   12329            0 :          FD_SET(c->send_sock, &readfds);
   12330              : 
   12331              :          struct timeval timeout;
   12332            0 :          timeout.tv_sec = 0;
   12333            0 :          timeout.tv_usec = 0;
   12334              : 
   12335              :          int status;
   12336              : 
   12337              : #ifdef OS_WINNT
   12338              :          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
   12339              : #else
   12340              :          do {
   12341            0 :             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
   12342            0 :          } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
   12343              : #endif
   12344              : 
   12345            0 :          if (!FD_ISSET(c->send_sock, &readfds)) {
   12346              :             // implicit unlock
   12347            0 :             continue;
   12348              :          }
   12349              : 
   12350              :          char buffer[64];
   12351              : 
   12352            0 :          status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
   12353              :          //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
   12354              : 
   12355            0 :          if (status < 0) {
   12356              : #ifndef OS_WINNT
   12357            0 :             if (errno == EAGAIN) { // still connected
   12358            0 :                ok = 1;
   12359              :             } else
   12360              : #endif
   12361              :             {
   12362              :                // connection error
   12363            0 :                cm_msg(MERROR, "rpc_client_check",
   12364              :                       "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
   12365              :                       c->client_name.c_str(),
   12366              :                       c->host_name.c_str(),
   12367            0 :                       errno, strerror(errno));
   12368            0 :                ok = 0;
   12369              :             }
   12370            0 :          } else if (status == 0) {
   12371              :             // connection closed by remote end without sending an EXIT message
   12372              :             // this can happen if the remote end has crashed, so this message
   12373              :             // is still necessary as a useful diagnostic for unexpected crashes
   12374              :             // of midas programs. K.O.
   12375            0 :             cm_msg(MINFO, "rpc_client_check", "RPC client connection to \"%s\" on host \"%s\" unexpectedly closed", c->client_name.c_str(), c->host_name.c_str());
   12376            0 :             ok = 0;
   12377              :          } else {
   12378              :             // read some data
   12379            0 :             ok = 1;
   12380            0 :             if (equal_ustring(buffer, "EXIT")) {
   12381              :                /* normal exit */
   12382            0 :                ok = 0;
   12383              :             }
   12384              :          }
   12385              : 
   12386            0 :          if (!ok) {
   12387              :             //printf("rpc_client_check: closing connection %d: ", i);
   12388              :             //c->print();
   12389              :             //printf("\n");
   12390              : 
   12391              :             // connection lost, close the socket
   12392            0 :             c->close_locked();
   12393              :          }
   12394              : 
   12395              :          // implicit unlock
   12396            0 :       }
   12397              :    }
   12398              : 
   12399              :    // implicit unlock of _client_connections_mutex
   12400            0 : }
   12401              : 
   12402              : 
   12403              : /********************************************************************/
   12404            0 : INT rpc_server_connect(const char *host_name, const char *exp_name)
   12405              : /********************************************************************\
   12406              : 
   12407              :   Routine: rpc_server_connect
   12408              : 
   12409              :   Purpose: Extablish a network connection to a remote MIDAS
   12410              :            server using a callback scheme.
   12411              : 
   12412              :   Input:
   12413              :     char *host_name         IP address of host to connect to.
   12414              : 
   12415              :     INT  port               TPC port to connect to.
   12416              : 
   12417              :     char *exp_name          Name of experiment to connect to. By using
   12418              :                             this name, several experiments (e.g. online
   12419              :                             DAQ and offline analysis) can run simultan-
   12420              :                             eously on the same host.
   12421              : 
   12422              :   Output:
   12423              :     none
   12424              : 
   12425              :   Function value:
   12426              :     RPC_SUCCESS              Successful completion
   12427              :     RPC_NET_ERROR            Error in socket call
   12428              :     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
   12429              :     CM_UNDEF_EXP             Undefined experiment on server
   12430              : 
   12431              : \********************************************************************/
   12432              : {
   12433              :    INT i, status;
   12434              :    INT remote_hw_type, hw_type;
   12435              :    char str[200], version[32], v1[32];
   12436              :    fd_set readfds;
   12437              :    struct timeval timeout;
   12438            0 :    int port = MIDAS_TCP_PORT;
   12439              :    char *s;
   12440              : 
   12441              : #ifdef OS_WINNT
   12442              :    {
   12443              :       WSADATA WSAData;
   12444              : 
   12445              :       /* Start windows sockets */
   12446              :       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
   12447              :          return RPC_NET_ERROR;
   12448              :    }
   12449              : #endif
   12450              : 
   12451              :    /* check if local connection */
   12452            0 :    if (host_name[0] == 0)
   12453            0 :       return RPC_SUCCESS;
   12454              : 
   12455              :    /* register system functions */
   12456            0 :    rpc_register_functions(rpc_get_internal_list(0), NULL);
   12457              : 
   12458              :    /* check if cm_connect_experiment was called */
   12459            0 :    if (_client_name.length() == 0) {
   12460            0 :       cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
   12461            0 :       return RPC_NOT_REGISTERED;
   12462              :    }
   12463              : 
   12464              :    /* check if connection already exists */
   12465            0 :    if (_server_connection.send_sock != 0)
   12466            0 :       return RPC_SUCCESS;
   12467              : 
   12468            0 :    _server_connection.host_name = host_name;
   12469            0 :    _server_connection.exp_name = exp_name;
   12470            0 :    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
   12471              : 
   12472            0 :    bool listen_localhost = false;
   12473              : 
   12474            0 :    if (strcmp(host_name, "localhost") == 0)
   12475            0 :       listen_localhost = true;
   12476              : 
   12477              :    int lsock1, lport1;
   12478              :    int lsock2, lport2;
   12479              :    int lsock3, lport3;
   12480              : 
   12481            0 :    std::string errmsg;
   12482              :    
   12483            0 :    status = ss_socket_listen_tcp(listen_localhost, 0, &lsock1, &lport1, &errmsg);
   12484              : 
   12485            0 :    if (status != SS_SUCCESS) {
   12486            0 :       cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
   12487            0 :       return RPC_NET_ERROR;
   12488              :    }
   12489              : 
   12490            0 :    status = ss_socket_listen_tcp(listen_localhost, 0, &lsock2, &lport2, &errmsg);
   12491              : 
   12492            0 :    if (status != SS_SUCCESS) {
   12493            0 :       cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
   12494            0 :       return RPC_NET_ERROR;
   12495              :    }
   12496              : 
   12497            0 :    status = ss_socket_listen_tcp(listen_localhost, 0, &lsock3, &lport3, &errmsg);
   12498              : 
   12499            0 :    if (status != SS_SUCCESS) {
   12500            0 :       cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
   12501            0 :       return RPC_NET_ERROR;
   12502              :    }
   12503              : 
   12504              :    /* extract port number from host_name */
   12505            0 :    mstrlcpy(str, host_name, sizeof(str));
   12506            0 :    s = strchr(str, ':');
   12507            0 :    if (s) {
   12508            0 :       *s = 0;
   12509            0 :       port = strtoul(s + 1, NULL, 0);
   12510              :    }
   12511              : 
   12512              :    int sock;
   12513              : 
   12514            0 :    status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
   12515              : 
   12516            0 :    if (status != SS_SUCCESS) {
   12517            0 :       cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
   12518            0 :       return RPC_NET_ERROR;
   12519              :    }
   12520              : 
   12521              :    /* connect to experiment */
   12522            0 :    if (exp_name[0] == 0)
   12523            0 :       sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
   12524              :    else
   12525            0 :       sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
   12526              : 
   12527            0 :    send(sock, str, strlen(str) + 1, 0);
   12528            0 :    i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
   12529            0 :    ss_socket_close(&sock);
   12530            0 :    if (i <= 0) {
   12531            0 :       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
   12532            0 :       return RPC_NET_ERROR;
   12533              :    }
   12534              : 
   12535            0 :    status = version[0] = 0;
   12536            0 :    sscanf(str, "%d %s", &status, version);
   12537              : 
   12538            0 :    if (status == 2) {
   12539              : /*  message "undefined experiment" should be displayed by application */
   12540            0 :       return CM_UNDEF_EXP;
   12541              :    }
   12542              : 
   12543              :    /* print warning if version patch level doesn't agree */
   12544            0 :    strcpy(v1, version);
   12545            0 :    if (strchr(v1, '.'))
   12546            0 :       if (strchr(strchr(v1, '.') + 1, '.'))
   12547            0 :          *strchr(strchr(v1, '.') + 1, '.') = 0;
   12548              : 
   12549            0 :    strcpy(str, cm_get_version());
   12550            0 :    if (strchr(str, '.'))
   12551            0 :       if (strchr(strchr(str, '.') + 1, '.'))
   12552            0 :          *strchr(strchr(str, '.') + 1, '.') = 0;
   12553              : 
   12554            0 :    if (strcmp(v1, str) != 0) {
   12555            0 :       cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
   12556              :              cm_get_version());
   12557              :    }
   12558              : 
   12559              :    /* wait for callback on send and recv socket with timeout */
   12560            0 :    FD_ZERO(&readfds);
   12561            0 :    FD_SET(lsock1, &readfds);
   12562            0 :    FD_SET(lsock2, &readfds);
   12563            0 :    FD_SET(lsock3, &readfds);
   12564              : 
   12565            0 :    timeout.tv_sec = _rpc_connect_timeout / 1000;
   12566            0 :    timeout.tv_usec = 0;
   12567              : 
   12568              :    do {
   12569            0 :       status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
   12570              : 
   12571              :       /* if an alarm signal was cought, restart select with reduced timeout */
   12572            0 :       if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
   12573            0 :          timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
   12574              : 
   12575            0 :    } while (status == -1);      /* dont return if an alarm signal was cought */
   12576              : 
   12577            0 :    if (!FD_ISSET(lsock1, &readfds)) {
   12578            0 :       cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
   12579            0 :       ss_socket_close(&lsock1);
   12580            0 :       ss_socket_close(&lsock2);
   12581            0 :       ss_socket_close(&lsock3);
   12582            0 :       return RPC_NET_ERROR;
   12583              :    }
   12584              : 
   12585            0 :    _server_connection.send_sock = accept(lsock1, NULL, NULL);
   12586            0 :    _server_connection.recv_sock = accept(lsock2, NULL, NULL);
   12587            0 :    _server_connection.event_sock = accept(lsock3, NULL, NULL);
   12588              : 
   12589            0 :    if (_server_connection.send_sock == -1 || _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
   12590            0 :       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
   12591            0 :       return RPC_NET_ERROR;
   12592              :    }
   12593              : 
   12594            0 :    ss_socket_close(&lsock1);
   12595            0 :    ss_socket_close(&lsock2);
   12596            0 :    ss_socket_close(&lsock3);
   12597              : 
   12598              :    /* set TCP_NODELAY option for better performance */
   12599            0 :    int flag = 1;
   12600            0 :    setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
   12601            0 :    setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
   12602              : 
   12603              :    /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
   12604            0 :    flag = 2 * 1024 * 1024;
   12605            0 :    status = setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
   12606            0 :    if (status != 0)
   12607            0 :       cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
   12608              : 
   12609              :    /* send local computer info */
   12610            0 :    std::string local_prog_name = rpc_get_name();
   12611            0 :    hw_type = rpc_get_hw_type();
   12612            0 :    sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
   12613              : 
   12614            0 :    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
   12615              : 
   12616              :    /* receive remote computer info */
   12617            0 :    i = recv_string(_server_connection.send_sock, str, sizeof(str), _rpc_connect_timeout);
   12618            0 :    if (i <= 0) {
   12619            0 :       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
   12620            0 :       return RPC_NET_ERROR;
   12621              :    }
   12622              : 
   12623            0 :    sscanf(str, "%d", &remote_hw_type);
   12624            0 :    _server_connection.remote_hw_type = remote_hw_type;
   12625              : 
   12626            0 :    ss_suspend_set_client_connection(&_server_connection);
   12627              : 
   12628            0 :    _rpc_is_remote = true;
   12629              : 
   12630            0 :    return RPC_SUCCESS;
   12631            0 : }
   12632              : 
   12633              : /********************************************************************/
   12634              : 
   12635            0 : static RPC_CLIENT_CONNECTION* rpc_get_locked_client_connection(HNDLE hConn)
   12636              : {
   12637            0 :    _client_connections_mutex.lock();
   12638            0 :    if (hConn >= 0 && hConn < (int)_client_connections.size()) {
   12639            0 :       RPC_CLIENT_CONNECTION* c = _client_connections[hConn];
   12640            0 :       if (c && c->connected) {
   12641            0 :          _client_connections_mutex.unlock();
   12642            0 :          c->mutex.lock();
   12643            0 :          if (!c->connected) {
   12644              :             // disconnected while we were waiting for the lock
   12645            0 :             c->mutex.unlock();
   12646            0 :             return NULL;
   12647              :          }
   12648            0 :          return c;
   12649              :       }
   12650              :    }
   12651            0 :    _client_connections_mutex.unlock();
   12652            0 :    return NULL;
   12653              : }
   12654              : 
   12655            2 : static void rpc_client_shutdown()
   12656              : {
   12657              :    /* close all open connections */
   12658              : 
   12659            2 :    _client_connections_mutex.lock();
   12660              : 
   12661            2 :    for (unsigned i = 0; i < _client_connections.size(); i++) {
   12662            0 :       RPC_CLIENT_CONNECTION* c = _client_connections[i];
   12663            0 :       if (c && c->connected) {
   12664            0 :          int index = c->index;
   12665              :          // must unlock the array, otherwise we hang -
   12666              :          // rpc_client_disconnect() will do rpc_call_client()
   12667              :          // which needs to lock the array to convert handle
   12668              :          // to connection pointer. Ouch! K.O. Dec 2020.
   12669            0 :          _client_connections_mutex.unlock();
   12670            0 :          rpc_client_disconnect(index, FALSE);
   12671            0 :          _client_connections_mutex.lock();
   12672              :       }
   12673              :    }
   12674              : 
   12675            2 :    for (unsigned i = 0; i < _client_connections.size(); i++) {
   12676            0 :       RPC_CLIENT_CONNECTION* c = _client_connections[i];
   12677              :       //printf("client connection %d %p\n", i, c);
   12678            0 :       if (c) {
   12679              :          //printf("client connection %d %p connected %d\n", i, c, c->connected);
   12680            0 :          if (!c->connected) {
   12681            0 :             delete c;
   12682            0 :             _client_connections[i] = NULL;
   12683              :          }
   12684              :       }
   12685              :    }
   12686              : 
   12687            2 :    _client_connections_mutex.unlock();
   12688              : 
   12689              :    /* close server connection from other clients */
   12690            2 :    for (unsigned i = 0; i < _server_acceptions.size(); i++) {
   12691            0 :       if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
   12692            0 :          send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
   12693            0 :          _server_acceptions[i]->close();
   12694              :       }
   12695              :    }
   12696            2 : }
   12697              : 
   12698              : /********************************************************************/
   12699            0 : INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
   12700              : /********************************************************************\
   12701              : 
   12702              :   Routine: rpc_client_disconnect
   12703              : 
   12704              :   Purpose: Close a rpc connection to a MIDAS client
   12705              : 
   12706              :   Input:
   12707              :     HNDLE  hConn           Handle of connection
   12708              :     BOOL   bShutdown       Shut down remote server if TRUE
   12709              : 
   12710              :   Output:
   12711              :     none
   12712              : 
   12713              :   Function value:
   12714              :    RPC_SUCCESS             Successful completion
   12715              : 
   12716              : \********************************************************************/
   12717              : {
   12718              :    /* notify server about exit */
   12719              :    
   12720              :    /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
   12721              :    
   12722            0 :    rpc_client_call(hConn, bShutdown ? (RPC_ID_SHUTDOWN | RPC_NO_REPLY) : (RPC_ID_EXIT | RPC_NO_REPLY));
   12723              : 
   12724            0 :    return RPC_SUCCESS;
   12725              : }
   12726              : 
   12727              : /********************************************************************/
   12728            0 : INT rpc_server_disconnect()
   12729              : /********************************************************************\
   12730              : 
   12731              :   Routine: rpc_server_disconnect
   12732              : 
   12733              :   Purpose: Close a rpc connection to a MIDAS server and close all
   12734              :            server connections from other clients
   12735              : 
   12736              :   Input:
   12737              :     none
   12738              : 
   12739              :   Output:
   12740              :     none
   12741              : 
   12742              :   Function value:
   12743              :    RPC_SUCCESS             Successful completion
   12744              :    RPC_NET_ERROR           Error in socket call
   12745              :    RPC_NO_CONNECTION       Maximum number of connections reached
   12746              : 
   12747              : \********************************************************************/
   12748              : {
   12749              :    static int rpc_server_disconnect_recursion_level = 0;
   12750              : 
   12751            0 :    if (rpc_server_disconnect_recursion_level)
   12752            0 :       return RPC_SUCCESS;
   12753              : 
   12754            0 :    rpc_server_disconnect_recursion_level = 1;
   12755              : 
   12756              :    /* flush remaining events */
   12757            0 :    rpc_flush_event();
   12758              : 
   12759              :    /* notify server about exit */
   12760            0 :    if (rpc_is_connected()) {
   12761            0 :       rpc_call(RPC_ID_EXIT);
   12762              :    }
   12763              : 
   12764              :    /* close sockets */
   12765            0 :    if (_server_connection.send_sock)
   12766            0 :       ss_socket_close(&_server_connection.send_sock);
   12767            0 :    if (_server_connection.recv_sock)
   12768            0 :       ss_socket_close(&_server_connection.recv_sock);
   12769            0 :    if (_server_connection.event_sock)
   12770            0 :       ss_socket_close(&_server_connection.event_sock);
   12771              : 
   12772            0 :    _server_connection.clear();
   12773              : 
   12774              :    /* remove semaphore */
   12775            0 :    if (_mutex_rpc)
   12776            0 :       ss_mutex_delete(_mutex_rpc);
   12777            0 :    _mutex_rpc = NULL;
   12778              : 
   12779            0 :    rpc_server_disconnect_recursion_level = 0;
   12780            0 :    return RPC_SUCCESS;
   12781              : }
   12782              : 
   12783              : /********************************************************************/
   12784         1273 : bool rpc_is_remote(void)
   12785              : /********************************************************************\
   12786              : 
   12787              :   Routine: rpc_is_remote
   12788              : 
   12789              :   Purpose: Return true if program is connected to a remote server
   12790              : 
   12791              :   Input:
   12792              :    none
   12793              : 
   12794              :   Output:
   12795              :     none
   12796              : 
   12797              :   Function value:
   12798              :     INT    true is remote client connected to mserver, false if local connection
   12799              : 
   12800              : \********************************************************************/
   12801              : {
   12802         1273 :    return _rpc_is_remote;
   12803              : }
   12804              : 
   12805              : /********************************************************************/
   12806            0 : bool rpc_is_connected(void)
   12807              : /********************************************************************\
   12808              : 
   12809              :   Routine: rpc_is_connected
   12810              : 
   12811              :   Purpose: Return true if connection to mserver is still open
   12812              : 
   12813              :   Input:
   12814              :    none
   12815              : 
   12816              :   Output:
   12817              :     none
   12818              : 
   12819              :   Function value:
   12820              :     INT    true if connection to mserver is still open, false if connection to mserver is already closed
   12821              : 
   12822              : \********************************************************************/
   12823              : {
   12824            0 :    return _server_connection.send_sock != 0;
   12825              : }
   12826              : 
   12827              : /********************************************************************/
   12828            0 : std::string rpc_get_mserver_hostname(void)
   12829              : /********************************************************************\
   12830              : 
   12831              :   Routine: rpc_get_mserver_hostname
   12832              : 
   12833              :   Purpose: Return the hostname of the mserver connection (host:port format)
   12834              : 
   12835              : \********************************************************************/
   12836              : {
   12837            0 :    return _server_connection.host_name;
   12838              : }
   12839              : 
   12840              : /********************************************************************/
   12841            6 : bool rpc_is_mserver(void)
   12842              : /********************************************************************\
   12843              : 
   12844              :   Routine: rpc_is_mserver
   12845              : 
   12846              :   Purpose: Return true if we are the mserver
   12847              : 
   12848              :   Function value:
   12849              :     INT    "true" if we are the mserver
   12850              : 
   12851              : \********************************************************************/
   12852              : {
   12853            6 :    return _mserver_acception != NULL;
   12854              : }
   12855              : 
   12856              : /********************************************************************/
   12857            2 : INT rpc_get_hw_type()
   12858              : /********************************************************************\
   12859              : 
   12860              :   Routine: rpc_get_hw_type
   12861              : 
   12862              :   Purpose: get hardware information
   12863              : 
   12864              :   Function value:
   12865              :     INT                     combination of DRI_xxx bits
   12866              : 
   12867              : \********************************************************************/
   12868              : {
   12869              :    {
   12870              :       {
   12871              :          INT tmp_type, size;
   12872              :          DWORD dummy;
   12873              :          unsigned char *p;
   12874              :          float f;
   12875              :          double d;
   12876              : 
   12877            2 :          tmp_type = 0;
   12878              : 
   12879              :          /* test pointer size */
   12880            2 :          size = sizeof(p);
   12881            2 :          if (size == 2)
   12882            0 :             tmp_type |= DRI_16;
   12883            2 :          if (size == 4)
   12884            0 :             tmp_type |= DRI_32;
   12885            2 :          if (size == 8)
   12886            2 :             tmp_type |= DRI_64;
   12887              : 
   12888              :          /* test if little or big endian machine */
   12889            2 :          dummy = 0x12345678;
   12890            2 :          p = (unsigned char *) &dummy;
   12891            2 :          if (*p == 0x78)
   12892            2 :             tmp_type |= DRI_LITTLE_ENDIAN;
   12893            0 :          else if (*p == 0x12)
   12894            0 :             tmp_type |= DRI_BIG_ENDIAN;
   12895              :          else
   12896            0 :             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
   12897              : 
   12898              :          /* floating point format */
   12899            2 :          f = (float) 1.2345;
   12900            2 :          dummy = 0;
   12901            2 :          memcpy(&dummy, &f, sizeof(f));
   12902            2 :          if ((dummy & 0xFF) == 0x19 &&
   12903            2 :              ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
   12904            2 :              && ((dummy >> 24) & 0xFF) == 0x3F)
   12905            2 :             tmp_type |= DRF_IEEE;
   12906            0 :          else if ((dummy & 0xFF) == 0x9E &&
   12907            0 :                   ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
   12908            0 :                   && ((dummy >> 24) & 0xFF) == 0x04)
   12909            0 :             tmp_type |= DRF_G_FLOAT;
   12910              :          else
   12911            0 :             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
   12912              : 
   12913            2 :          d = (double) 1.2345;
   12914            2 :          dummy = 0;
   12915            2 :          memcpy(&dummy, &d, sizeof(f));
   12916            2 :          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
   12917            2 :              ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
   12918            2 :              && ((dummy >> 24) & 0xFF) == 0x12)
   12919            2 :             tmp_type |= DRF_IEEE;
   12920            0 :          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
   12921            0 :                   ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
   12922            0 :                   && ((dummy >> 24) & 0xFF) == 0x3F)
   12923            0 :             tmp_type |= DRF_IEEE;
   12924            0 :          else if ((dummy & 0xFF) == 0x13 &&
   12925            0 :                   ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
   12926            0 :                   && ((dummy >> 24) & 0xFF) == 0xC0)
   12927            0 :             tmp_type |= DRF_G_FLOAT;
   12928            0 :          else if ((dummy & 0xFF) == 0x9E &&
   12929            0 :                   ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
   12930            0 :                   && ((dummy >> 24) & 0xFF) == 0x04)
   12931            0 :             cm_msg(MERROR, "rpc_get_option",
   12932              :                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
   12933              :          else
   12934            0 :             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
   12935              : 
   12936            2 :          return tmp_type;
   12937              :       }
   12938              :    }
   12939              : }
   12940              : 
   12941              : /**dox***************************************************************/
   12942              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   12943              : 
   12944              : /********************************************************************/
   12945              : /**
   12946              : Set RPC option
   12947              : @param hConn              RPC connection handle, -1 for server connection, -2 for rpc connect timeout
   12948              : @param item               One of RPC_Oxxx
   12949              : @param value              Value to set
   12950              : @return RPC_SUCCESS
   12951              : */
   12952              : #if 0
   12953              : INT rpc_set_option(HNDLE hConn, INT item, INT value) {
   12954              :    switch (item) {
   12955              :       case RPC_OTIMEOUT:
   12956              :          if (hConn == -1)
   12957              :             _server_connection.rpc_timeout = value;
   12958              :          else if (hConn == -2)
   12959              :             _rpc_connect_timeout = value;
   12960              :          else {
   12961              :             RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
   12962              :             if (c) {
   12963              :                c->rpc_timeout = value;
   12964              :                c->mutex.unlock();
   12965              :             }
   12966              :          }
   12967              :          break;
   12968              : 
   12969              :       case RPC_NODELAY:
   12970              :          if (hConn == -1) {
   12971              :             setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
   12972              :          } else {
   12973              :             RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
   12974              :             if (c) {
   12975              :                setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
   12976              :                c->mutex.unlock();
   12977              :             }
   12978              :          }
   12979              :          break;
   12980              : 
   12981              :       default:
   12982              :          cm_msg(MERROR, "rpc_set_option", "invalid argument");
   12983              :          break;
   12984              :    }
   12985              : 
   12986              :    return 0;
   12987              : }
   12988              : #endif
   12989              : 
   12990              : /********************************************************************/
   12991              : /**
   12992              : Get RPC timeout
   12993              : @param hConn              RPC connection handle, RPC_HNDLE_MSERVER for mserver connection, RPC_HNDLE_CONNECT for rpc connect timeout
   12994              : @return timeout value
   12995              : */
   12996            0 : INT rpc_get_timeout(HNDLE hConn)
   12997              : {
   12998            0 :    if (hConn == RPC_HNDLE_MSERVER) {
   12999            0 :       return _server_connection.rpc_timeout;
   13000            0 :    } else if (hConn == RPC_HNDLE_CONNECT) {
   13001            0 :       return _rpc_connect_timeout;
   13002              :    } else {
   13003            0 :       RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
   13004            0 :       if (c) {
   13005            0 :          int timeout = c->rpc_timeout;
   13006            0 :          c->mutex.unlock();
   13007            0 :          return timeout;
   13008              :       }
   13009              :    }
   13010            0 :    return 0;
   13011              : }
   13012              : 
   13013              : /********************************************************************/
   13014              : /**
   13015              : Set RPC timeout
   13016              : @param hConn              RPC connection handle, RPC_HNDLE_MSERVER for mserver connection, RPC_HNDLE_CONNECT for rpc connect timeout
   13017              : @param timeout_msec       RPC timeout in milliseconds
   13018              : @param old_timeout_msec   returns old value of RPC timeout in milliseconds
   13019              : @return RPC_SUCCESS
   13020              : */
   13021            0 : INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int* old_timeout_msec)
   13022              : {
   13023              :    //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
   13024              : 
   13025            0 :    if (hConn == RPC_HNDLE_MSERVER) {
   13026            0 :       if (old_timeout_msec)
   13027            0 :          *old_timeout_msec = _server_connection.rpc_timeout;
   13028            0 :       _server_connection.rpc_timeout = timeout_msec;
   13029            0 :    } else if (hConn == RPC_HNDLE_CONNECT) {
   13030            0 :       if (old_timeout_msec)
   13031            0 :          *old_timeout_msec = _rpc_connect_timeout;
   13032            0 :       _rpc_connect_timeout = timeout_msec;
   13033              :    } else {
   13034            0 :       RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
   13035            0 :       if (c) {
   13036            0 :          if (old_timeout_msec)
   13037            0 :             *old_timeout_msec = c->rpc_timeout;
   13038            0 :          c->rpc_timeout = timeout_msec;
   13039            0 :          c->mutex.unlock();
   13040              :       } else {
   13041            0 :          if (old_timeout_msec)
   13042            0 :             *old_timeout_msec = 0;
   13043              :       }
   13044              :    }
   13045            0 :    return RPC_SUCCESS;
   13046              : }
   13047              : 
   13048              : 
   13049              : /**dox***************************************************************/
   13050              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   13051              : 
   13052              : /********************************************************************/
   13053            0 : INT rpc_get_convert_flags(void)
   13054              : /********************************************************************\
   13055              : 
   13056              :   Routine: rpc_get_convert_flags
   13057              : 
   13058              :   Purpose: Get RPC convert_flags for the mserver connection
   13059              : 
   13060              :   Function value:
   13061              :     INT                     Actual option
   13062              : 
   13063              : \********************************************************************/
   13064              : {
   13065            0 :    if (_mserver_acception)
   13066            0 :       return _mserver_acception->convert_flags;
   13067              :    else
   13068            0 :       return 0;
   13069              : }
   13070              : 
   13071              : static std::string _mserver_path;
   13072              : 
   13073              : /********************************************************************/
   13074            0 : const char *rpc_get_mserver_path()
   13075              : /********************************************************************\
   13076              : 
   13077              :   Routine: rpc_get_mserver_path()
   13078              : 
   13079              :   Purpose: Get path of the mserver executable
   13080              : 
   13081              : \********************************************************************/
   13082              : {
   13083            0 :    return _mserver_path.c_str();
   13084              : }
   13085              : 
   13086              : /********************************************************************/
   13087            0 : INT rpc_set_mserver_path(const char *path)
   13088              : /********************************************************************\
   13089              : 
   13090              :   Routine: rpc_set_mserver_path
   13091              : 
   13092              :   Purpose: Remember the path of the mserver executable
   13093              : 
   13094              :   Input:
   13095              :    char *path               Full path of the mserver executable
   13096              : 
   13097              :   Function value:
   13098              :     RPC_SUCCESS             Successful completion
   13099              : 
   13100              : \********************************************************************/
   13101              : {
   13102            0 :    _mserver_path = path;
   13103            0 :    return RPC_SUCCESS;
   13104              : }
   13105              : 
   13106              : /********************************************************************/
   13107           19 : std::string rpc_get_name()
   13108              : /********************************************************************\
   13109              : 
   13110              :   Routine: rpc_get_name
   13111              : 
   13112              :   Purpose: Get name set by rpc_set_name
   13113              : 
   13114              :   Input:
   13115              :     none
   13116              : 
   13117              :   Output:
   13118              :     char*  name             The location pointed by *name receives a
   13119              :                             copy of the _prog_name
   13120              : 
   13121              :   Function value:
   13122              :     RPC_SUCCESS             Successful completion
   13123              : 
   13124              : \********************************************************************/
   13125              : {
   13126           19 :    return _client_name;
   13127              : }
   13128              : 
   13129              : 
   13130              : /********************************************************************/
   13131            4 : INT rpc_set_name(const char *name)
   13132              : /********************************************************************\
   13133              : 
   13134              :   Routine: rpc_set_name
   13135              : 
   13136              :   Purpose: Set name of actual program for further rpc connections
   13137              : 
   13138              :   Input:
   13139              :    char *name               Program name, up to NAME_LENGTH chars,
   13140              :                             no blanks
   13141              : 
   13142              :   Output:
   13143              :     none
   13144              : 
   13145              :   Function value:
   13146              :     RPC_SUCCESS             Successful completion
   13147              : 
   13148              : \********************************************************************/
   13149              : {
   13150            4 :    _client_name = name;
   13151              : 
   13152            4 :    return RPC_SUCCESS;
   13153              : }
   13154              : 
   13155              : 
   13156              : /********************************************************************/
   13157            0 : INT rpc_set_debug(void (*func)(const char *), INT mode)
   13158              : /********************************************************************\
   13159              : 
   13160              :   Routine: rpc_set_debug
   13161              : 
   13162              :   Purpose: Set a function which is called on every RPC call to
   13163              :            display the function name and parameters of the RPC
   13164              :            call.
   13165              : 
   13166              :   Input:
   13167              :    void *func(char*)        Pointer to function.
   13168              :    INT  mode                Debug mode
   13169              : 
   13170              :   Output:
   13171              :     none
   13172              : 
   13173              :   Function value:
   13174              :     RPC_SUCCESS             Successful completion
   13175              : 
   13176              : \********************************************************************/
   13177              : {
   13178            0 :    _debug_print = func;
   13179            0 :    _debug_mode = mode;
   13180            0 :    return RPC_SUCCESS;
   13181              : }
   13182              : 
   13183              : /********************************************************************/
   13184            0 : void rpc_debug_printf(const char *format, ...)
   13185              : /********************************************************************\
   13186              : 
   13187              :   Routine: rpc_debug_print
   13188              : 
   13189              :   Purpose: Calls function set via rpc_set_debug to output a string.
   13190              : 
   13191              :   Input:
   13192              :    char *str                Debug string
   13193              : 
   13194              :   Output:
   13195              :     none
   13196              : 
   13197              : \********************************************************************/
   13198              : {
   13199              :    va_list argptr;
   13200              :    char str[1000];
   13201              : 
   13202            0 :    if (_debug_mode) {
   13203            0 :       va_start(argptr, format);
   13204            0 :       vsprintf(str, (char *) format, argptr);
   13205            0 :       va_end(argptr);
   13206              : 
   13207            0 :       if (_debug_print) {
   13208            0 :          strcat(str, "\n");
   13209            0 :          _debug_print(str);
   13210              :       } else
   13211            0 :          puts(str);
   13212              :    }
   13213            0 : }
   13214              : 
   13215              : /********************************************************************/
   13216            0 : void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg) {
   13217            0 :    switch (arg_type) {
   13218              :       /* On the stack, the minimum parameter size is sizeof(int).
   13219              :          To avoid problems on little endian systems, treat all
   13220              :          smaller parameters as int's */
   13221            0 :       case TID_UINT8:
   13222              :       case TID_INT8:
   13223              :       case TID_CHAR:
   13224              :       case TID_UINT16:
   13225              :       case TID_INT16:
   13226            0 :          *((int *) arg) = va_arg(*arg_ptr, int);
   13227            0 :          break;
   13228              : 
   13229            0 :       case TID_INT32:
   13230              :       case TID_BOOL:
   13231            0 :          *((INT *) arg) = va_arg(*arg_ptr, INT);
   13232            0 :          break;
   13233              : 
   13234            0 :       case TID_UINT32:
   13235            0 :          *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
   13236            0 :          break;
   13237              : 
   13238              :          /* float variables are passed as double by the compiler */
   13239            0 :       case TID_FLOAT:
   13240            0 :          *((float *) arg) = (float) va_arg(*arg_ptr, double);
   13241            0 :          break;
   13242              : 
   13243            0 :       case TID_DOUBLE:
   13244            0 :          *((double *) arg) = va_arg(*arg_ptr, double);
   13245            0 :          break;
   13246              : 
   13247            0 :       case TID_ARRAY:
   13248            0 :          *((char **) arg) = va_arg(*arg_ptr, char *);
   13249            0 :          break;
   13250              :    }
   13251            0 : }
   13252              : 
   13253              : /********************************************************************/
   13254            0 : static void rpc_call_encode(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
   13255              : {
   13256            0 :    bool debug = false;
   13257              : 
   13258            0 :    if (debug) {
   13259            0 :       printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
   13260            0 :       for (int i=0; rl.param[i].tid != 0; i++) {
   13261            0 :          int tid = rl.param[i].tid;
   13262            0 :          int flags = rl.param[i].flags;
   13263            0 :          int n = rl.param[i].n;
   13264            0 :          printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
   13265              :       }
   13266              :    }
   13267              : 
   13268              :    char args[MAX_RPC_PARAMS][8];
   13269              : 
   13270            0 :    for (int i=0; rl.param[i].tid != 0; i++) {
   13271            0 :       int tid = rl.param[i].tid;
   13272            0 :       int flags = rl.param[i].flags;
   13273            0 :       int arg_type = 0;
   13274              : 
   13275            0 :       bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
   13276            0 :          (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
   13277            0 :          tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
   13278              :       
   13279            0 :       if (bpointer)
   13280            0 :          arg_type = TID_ARRAY;
   13281              :       else
   13282            0 :          arg_type = tid;
   13283              :       
   13284              :       /* floats are passed as doubles, at least under NT */
   13285            0 :       if (tid == TID_FLOAT && !bpointer)
   13286            0 :          arg_type = TID_DOUBLE;
   13287              :       
   13288              :       //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
   13289              : 
   13290            0 :       rpc_va_arg(&ap, arg_type, args[i]);
   13291              :    }
   13292              : 
   13293            0 :    size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
   13294            0 :    char* buf = (char *)malloc(buf_size);
   13295            0 :    assert(buf);
   13296              : 
   13297            0 :    (*nc) = (NET_COMMAND*) buf;
   13298              : 
   13299              :    /* find out if we are on a big endian system */
   13300            0 :    bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
   13301              : 
   13302            0 :    char* param_ptr = (*nc)->param;
   13303              : 
   13304            0 :    for (int i=0; rl.param[i].tid != 0; i++) {
   13305            0 :       int tid = rl.param[i].tid;
   13306            0 :       int flags = rl.param[i].flags;
   13307            0 :       int arg_type = 0;
   13308              : 
   13309            0 :       bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
   13310            0 :                  (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
   13311            0 :                  tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
   13312              : 
   13313            0 :       if (bpointer)
   13314            0 :          arg_type = TID_ARRAY;
   13315              :       else
   13316            0 :          arg_type = tid;
   13317              : 
   13318              :       /* floats are passed as doubles, at least under NT */
   13319            0 :       if (tid == TID_FLOAT && !bpointer)
   13320            0 :          arg_type = TID_DOUBLE;
   13321              : 
   13322              :       /* get pointer to argument */
   13323              :       //char arg[8];
   13324              :       //rpc_va_arg(&ap, arg_type, arg);
   13325              : 
   13326            0 :       char* arg = args[i];
   13327              : 
   13328              :       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
   13329            0 :       if (bbig) {
   13330            0 :          if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
   13331            0 :             arg[0] = arg[3];
   13332              :          }
   13333            0 :          if (tid == TID_UINT16 || tid == TID_INT16) {
   13334            0 :             arg[0] = arg[2];
   13335            0 :             arg[1] = arg[3];
   13336              :          }
   13337              :       }
   13338              : 
   13339            0 :       if (flags & RPC_IN) {
   13340            0 :          int arg_size = 0;
   13341              : 
   13342            0 :          if (bpointer)
   13343            0 :             arg_size = rpc_tid_size(tid);
   13344              :          else
   13345            0 :             arg_size = rpc_tid_size(arg_type);
   13346              : 
   13347              :          /* for strings, the argument size depends on the string length */
   13348            0 :          if (tid == TID_STRING || tid == TID_LINK) {
   13349            0 :             arg_size = 1 + strlen((char *) *((char **) arg));
   13350              :          }
   13351              : 
   13352              :          /* for varibale length arrays, the size is given by
   13353              :             the next parameter on the stack */
   13354            0 :          if (flags & RPC_VARARRAY) {
   13355              :             //va_list aptmp;
   13356              :             ////memcpy(&aptmp, &ap, sizeof(ap));
   13357              :             //va_copy(aptmp, ap);
   13358              : 
   13359              :             //char arg_tmp[8];
   13360              :             //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
   13361              : 
   13362            0 :             const char* arg_tmp = args[i+1];
   13363              : 
   13364              :             /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
   13365            0 :             if (flags & RPC_OUT)
   13366            0 :                arg_size = *((INT *) *((void **) arg_tmp));
   13367              :             else
   13368            0 :                arg_size = *((INT *) arg_tmp);
   13369              : 
   13370            0 :             *((INT *) param_ptr) = ALIGN8(arg_size);
   13371            0 :             param_ptr += ALIGN8(sizeof(INT));
   13372              : 
   13373              :             //va_end(aptmp);
   13374              :          }
   13375              : 
   13376            0 :          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
   13377            0 :             arg_size = rl.param[i].n;
   13378              : 
   13379              :          /* always align parameter size */
   13380            0 :          int param_size = ALIGN8(arg_size);
   13381              : 
   13382              :          {
   13383            0 :             size_t param_offset = (char *) param_ptr - (char *)(*nc);
   13384              : 
   13385            0 :             if (param_offset + param_size + 16 > buf_size) {
   13386            0 :                size_t new_size = param_offset + param_size + 1024;
   13387              :                //printf("resize nc %zu to %zu\n", buf_size, new_size);
   13388            0 :                buf = (char *) realloc(buf, new_size);
   13389            0 :                assert(buf);
   13390            0 :                buf_size = new_size;
   13391            0 :                (*nc) = (NET_COMMAND*) buf;
   13392            0 :                param_ptr = buf + param_offset;
   13393              :             }
   13394              :          }
   13395              : 
   13396            0 :          if (bpointer) {
   13397            0 :             if (debug) {
   13398            0 :                printf("encode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, memcpy pointer %d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
   13399              :             }
   13400            0 :             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
   13401            0 :          } else if (tid == TID_FLOAT) {
   13402            0 :             if (debug) {
   13403            0 :                printf("encode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, double->float\n", i, flags, tid, arg_type, arg_size, param_size);
   13404              :             }
   13405              :             /* floats are passed as doubles on most systems */
   13406            0 :             *((float *) param_ptr) = (float) *((double *) arg);
   13407              :          } else {
   13408            0 :             if (debug) {
   13409            0 :                printf("encode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, memcpy %d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
   13410              :             }
   13411            0 :             memcpy(param_ptr, arg, arg_size);
   13412              :          }
   13413              : 
   13414            0 :          param_ptr += param_size;
   13415              :       }
   13416              :    }
   13417              : 
   13418            0 :    (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
   13419              : 
   13420            0 :    if (debug)
   13421            0 :       printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
   13422            0 : }
   13423              : 
   13424              : /********************************************************************/
   13425            0 : static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
   13426              : {
   13427            0 :    bool debug = false;
   13428              : 
   13429            0 :    if (debug)
   13430            0 :       printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
   13431              : 
   13432              :    /* extract result variables and place it to argument list */
   13433              : 
   13434            0 :    const char* param_ptr = buf;
   13435              : 
   13436            0 :    for (int i = 0; rl.param[i].tid != 0; i++) {
   13437            0 :       int tid = rl.param[i].tid;
   13438            0 :       int flags = rl.param[i].flags;
   13439            0 :       int arg_type = 0;
   13440              : 
   13441            0 :       bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
   13442            0 :                  (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
   13443            0 :                  tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
   13444              : 
   13445            0 :       if (bpointer)
   13446            0 :          arg_type = TID_ARRAY;
   13447              :       else
   13448            0 :          arg_type = rl.param[i].tid;
   13449              : 
   13450            0 :       if (tid == TID_FLOAT && !bpointer)
   13451            0 :          arg_type = TID_DOUBLE;
   13452              : 
   13453              :       char arg[8];
   13454            0 :       rpc_va_arg(&ap, arg_type, arg);
   13455              : 
   13456            0 :       if (rl.param[i].flags & RPC_OUT) {
   13457              : 
   13458            0 :          if (param_ptr == NULL) {
   13459            0 :             cm_msg(MERROR, "rpc_call_decode", "routine \"%s\": no data in RPC reply, needed to decode an RPC_OUT parameter. param_ptr is NULL", rl.name);
   13460            0 :             return RPC_NET_ERROR;
   13461              :          }
   13462              : 
   13463            0 :          tid = rl.param[i].tid;
   13464            0 :          int arg_size = rpc_tid_size(tid);
   13465              : 
   13466            0 :          if (tid == TID_STRING || tid == TID_LINK)
   13467            0 :             arg_size = strlen((char *) (param_ptr)) + 1;
   13468              : 
   13469            0 :          if (flags & RPC_VARARRAY) {
   13470            0 :             arg_size = *((INT *) param_ptr);
   13471            0 :             param_ptr += ALIGN8(sizeof(INT));
   13472              :          }
   13473              : 
   13474            0 :          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
   13475            0 :             arg_size = rl.param[i].n;
   13476              : 
   13477              :          /* parameter size is always aligned */
   13478            0 :          int param_size = ALIGN8(arg_size);
   13479              : 
   13480              :          /* return parameters are always pointers */
   13481            0 :          if (*((char **) arg)) {
   13482            0 :             if (debug)
   13483            0 :                printf("decode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, memcpy %d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
   13484            0 :             memcpy((void *) *((char **) arg), param_ptr, arg_size);
   13485              :          }
   13486              : 
   13487            0 :          param_ptr += param_size;
   13488              :       }
   13489              :    }
   13490              : 
   13491            0 :    return RPC_SUCCESS;
   13492              : }
   13493              : 
   13494              : /********************************************************************/
   13495            0 : INT rpc_client_call(HNDLE hConn, DWORD routine_id, ...)
   13496              : /********************************************************************\
   13497              : 
   13498              :   Routine: rpc_client_call
   13499              : 
   13500              :   Purpose: Call a function on a MIDAS client
   13501              : 
   13502              :   Input:
   13503              :     INT  hConn              Client connection
   13504              :     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
   13505              : 
   13506              :     ...                     variable argument list
   13507              : 
   13508              :   Output:
   13509              :     (depends on argument list)
   13510              : 
   13511              :   Function value:
   13512              :     RPC_SUCCESS             Successful completion
   13513              :     RPC_NET_ERROR           Error in socket call
   13514              :     RPC_NO_CONNECTION       No active connection
   13515              :     RPC_TIMEOUT             Timeout in RPC call
   13516              :     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
   13517              :     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
   13518              : 
   13519              : \********************************************************************/
   13520              : {
   13521            0 :    RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
   13522              : 
   13523            0 :    if (!c) {
   13524            0 :       cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
   13525            0 :       return RPC_NO_CONNECTION;
   13526              :    }
   13527              : 
   13528              :    //printf("rpc_client_call: handle %d, connection: ", hConn);
   13529              :    //c->print();
   13530              :    //printf("\n");
   13531              : 
   13532              :    INT i, status;
   13533              : 
   13534            0 :    BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
   13535            0 :    routine_id &= ~RPC_NO_REPLY;
   13536              : 
   13537              :    //if (rpc_no_reply)
   13538              :    //   printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
   13539              : 
   13540              :    // make local copy of the client name just in case _client_connection is erased by another thread
   13541              : 
   13542              :    /* find rpc_index */
   13543              : 
   13544            0 :    int rpc_index = -1;
   13545            0 :    const char *rpc_name = NULL;
   13546              :    RPC_LIST rpc_entry;
   13547              : 
   13548            0 :    rpc_list_mutex.lock();
   13549            0 :    for (size_t i = 0; i < rpc_list.size(); i++) {
   13550            0 :       if (rpc_list[i].id == (int) routine_id) {
   13551            0 :          rpc_index = i;
   13552            0 :          rpc_name = rpc_list[rpc_index].name;
   13553            0 :          rpc_entry = rpc_list[rpc_index];
   13554            0 :          break;
   13555              :       }
   13556              :    }
   13557            0 :    rpc_list_mutex.unlock();
   13558              : 
   13559            0 :    if (rpc_index < 0) {
   13560            0 :       cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" with invalid RPC ID %d", c->client_name.c_str(), c->host_name.c_str(), routine_id);
   13561            0 :       c->mutex.unlock();
   13562            0 :       return RPC_INVALID_ID;
   13563              :    }
   13564              : 
   13565            0 :    NET_COMMAND *nc = NULL;
   13566              : 
   13567              :    /* examine variable argument list and convert it to parameter array */
   13568              :    va_list ap;
   13569            0 :    va_start(ap, routine_id);
   13570              : 
   13571            0 :    rpc_call_encode(ap, rpc_entry, &nc);
   13572              : 
   13573            0 :    va_end(ap);
   13574              : 
   13575            0 :    nc->header.routine_id = routine_id;
   13576              : 
   13577            0 :    if (rpc_no_reply)
   13578            0 :       nc->header.routine_id |= RPC_NO_REPLY;
   13579              : 
   13580            0 :    int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
   13581              : 
   13582              :    /* in FAST TCP mode, only send call and return immediately */
   13583            0 :    if (rpc_no_reply) {
   13584            0 :       i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
   13585              : 
   13586            0 :       if (i != send_size) {
   13587            0 :          cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": send_tcp() failed", c->client_name.c_str(), c->host_name.c_str(), rpc_name);
   13588            0 :          free(nc);
   13589            0 :          c->mutex.unlock();
   13590            0 :          return RPC_NET_ERROR;
   13591              :       }
   13592              : 
   13593            0 :       free(nc);
   13594              : 
   13595            0 :       if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
   13596              :          //printf("rpc_client_call: routine_id %d is RPC_ID_EXIT %d or RPC_ID_SHUTDOWN %d, closing connection: ", routine_id, RPC_ID_EXIT, RPC_ID_SHUTDOWN);
   13597              :          //c->print();
   13598              :          //printf("\n");
   13599            0 :          c->close_locked();
   13600              :       }
   13601              : 
   13602            0 :       c->mutex.unlock();
   13603            0 :       return RPC_SUCCESS;
   13604              :    }
   13605              : 
   13606              :    /* in TCP mode, send and wait for reply on send socket */
   13607            0 :    i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
   13608            0 :    if (i != send_size) {
   13609            0 :       cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": send_tcp() failed", c->client_name.c_str(), c->host_name.c_str(), rpc_name);
   13610            0 :       c->mutex.unlock();
   13611            0 :       return RPC_NET_ERROR;
   13612              :    }
   13613              : 
   13614            0 :    free(nc);
   13615            0 :    nc = NULL;
   13616              : 
   13617            0 :    bool restore_watchdog_timeout = false;
   13618              :    BOOL watchdog_call;
   13619              :    DWORD watchdog_timeout;
   13620            0 :    cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
   13621              : 
   13622              :    //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
   13623              : 
   13624            0 :    if (c->rpc_timeout >= (int) watchdog_timeout) {
   13625            0 :       restore_watchdog_timeout = true;
   13626            0 :       cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
   13627              :    }
   13628              : 
   13629            0 :    DWORD rpc_status = 0;
   13630            0 :    DWORD buf_size = 0;
   13631            0 :    char* buf = NULL;
   13632              : 
   13633              :    /* receive result on send socket */
   13634            0 :    status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
   13635              : 
   13636            0 :    if (restore_watchdog_timeout) {
   13637            0 :       cm_set_watchdog_params(watchdog_call, watchdog_timeout);
   13638              :    }
   13639              : 
   13640            0 :    if (status == SS_TIMEOUT) {
   13641            0 :       cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": timeout waiting for reply", c->client_name.c_str(), c->host_name.c_str(), rpc_name);
   13642            0 :       if (buf)
   13643            0 :          free(buf);
   13644            0 :       c->mutex.unlock();
   13645            0 :       return RPC_TIMEOUT;
   13646              :    }
   13647              : 
   13648            0 :    if (status != SS_SUCCESS) {
   13649            0 :       cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": error, ss_recv_net_command() status %d", c->client_name.c_str(), c->host_name.c_str(), rpc_name, status);
   13650            0 :       if (buf)
   13651            0 :          free(buf);
   13652            0 :       c->mutex.unlock();
   13653            0 :       return RPC_NET_ERROR;
   13654              :    }
   13655              : 
   13656            0 :    c->mutex.unlock();
   13657              : 
   13658            0 :    if (rpc_status == RPC_INVALID_ID) {
   13659            0 :       cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": error, unknown RPC, status %d", c->client_name.c_str(), c->host_name.c_str(), rpc_name, rpc_status);
   13660            0 :       if (buf)
   13661            0 :          free(buf);
   13662            0 :       return rpc_status;
   13663              :    }
   13664              : 
   13665              :    /* extract result variables and place it to argument list */
   13666              : 
   13667            0 :    va_start(ap, routine_id);
   13668              : 
   13669            0 :    status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
   13670              : 
   13671            0 :    if (status != RPC_SUCCESS) {
   13672            0 :       rpc_status = status;
   13673              :    }
   13674              : 
   13675            0 :    va_end(ap);
   13676              : 
   13677            0 :    if (buf)
   13678            0 :       free(buf);
   13679            0 :    buf = NULL;
   13680            0 :    buf_size = 0;
   13681              : 
   13682            0 :    return rpc_status;
   13683              : }
   13684              : 
   13685              : /********************************************************************/
   13686            0 : INT rpc_call(DWORD routine_id, ...)
   13687              : /********************************************************************\
   13688              : 
   13689              :   Routine: rpc_call
   13690              : 
   13691              :   Purpose: Call a function on a MIDAS server
   13692              : 
   13693              :   Input:
   13694              :     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
   13695              : 
   13696              :     ...                     variable argument list
   13697              : 
   13698              :   Output:
   13699              :     (depends on argument list)
   13700              : 
   13701              :   Function value:
   13702              :     RPC_SUCCESS             Successful completion
   13703              :     RPC_NET_ERROR           Error in socket call
   13704              :     RPC_NO_CONNECTION       No active connection
   13705              :     RPC_TIMEOUT             Timeout in RPC call
   13706              :     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
   13707              :     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
   13708              : 
   13709              : \********************************************************************/
   13710              : {
   13711              :    va_list ap;
   13712              :    INT i, status;
   13713              : 
   13714            0 :    BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
   13715            0 :    routine_id &= ~RPC_NO_REPLY;
   13716              : 
   13717              :    //if (rpc_no_reply)
   13718              :    //   printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
   13719              : 
   13720            0 :    int send_sock = _server_connection.send_sock;
   13721            0 :    int rpc_timeout = _server_connection.rpc_timeout;
   13722              : 
   13723            0 :    if (!send_sock) {
   13724            0 :       fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
   13725            0 :       return RPC_NET_ERROR;
   13726              :    }
   13727              : 
   13728            0 :    if (!_mutex_rpc) {
   13729              :       /* create a local mutex for multi-threaded applications */
   13730            0 :       ss_mutex_create(&_mutex_rpc, FALSE);
   13731              :    }
   13732              : 
   13733            0 :    status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
   13734            0 :    if (status != SS_SUCCESS) {
   13735            0 :       cm_msg(MERROR, "rpc_call", "Mutex timeout");
   13736            0 :       return RPC_MUTEX_TIMEOUT;
   13737              :    }
   13738              : 
   13739              :    /* find rpc definition */
   13740              : 
   13741            0 :    int idx = -1;
   13742            0 :    const char* rpc_name = NULL;
   13743              :    RPC_LIST rpc_entry;
   13744              : 
   13745            0 :    rpc_list_mutex.lock();
   13746              : 
   13747            0 :    for (size_t i = 0; i < rpc_list.size(); i++) {
   13748            0 :       if (rpc_list[i].id == (int) routine_id) {
   13749            0 :          idx = i;
   13750            0 :          rpc_name = rpc_list[idx].name;
   13751            0 :          rpc_entry = rpc_list[idx];
   13752            0 :          break;
   13753              :       }
   13754              :    }
   13755              : 
   13756            0 :    rpc_list_mutex.unlock();
   13757              : 
   13758            0 :    if (idx < 0) {
   13759            0 :       ss_mutex_release(_mutex_rpc);
   13760            0 :       cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
   13761            0 :       return RPC_INVALID_ID;
   13762              :    }
   13763              : 
   13764              :    /* prepare output buffer */
   13765              : 
   13766            0 :    NET_COMMAND* nc = NULL;
   13767              : 
   13768              :    /* examine variable argument list and convert it to parameter array */
   13769            0 :    va_start(ap, routine_id);
   13770              : 
   13771            0 :    rpc_call_encode(ap, rpc_entry, &nc);
   13772              : 
   13773            0 :    va_end(ap);
   13774              : 
   13775            0 :    nc->header.routine_id = routine_id;
   13776              : 
   13777            0 :    if (rpc_no_reply)
   13778            0 :       nc->header.routine_id |= RPC_NO_REPLY;
   13779              : 
   13780            0 :    int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
   13781              : 
   13782              :    /* do not wait for reply if requested RPC_NO_REPLY */
   13783            0 :    if (rpc_no_reply) {
   13784            0 :       i = send_tcp(send_sock, (char *) nc, send_size, 0);
   13785              : 
   13786            0 :       if (i != send_size) {
   13787            0 :          ss_mutex_release(_mutex_rpc);
   13788            0 :          cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
   13789            0 :          free(nc);
   13790            0 :          return RPC_NET_ERROR;
   13791              :       }
   13792              : 
   13793            0 :       ss_mutex_release(_mutex_rpc);
   13794            0 :       free(nc);
   13795            0 :       return RPC_SUCCESS;
   13796              :    }
   13797              : 
   13798              :    /* in TCP mode, send and wait for reply on send socket */
   13799            0 :    i = send_tcp(send_sock, (char *) nc, send_size, 0);
   13800            0 :    if (i != send_size) {
   13801            0 :       ss_mutex_release(_mutex_rpc);
   13802            0 :       cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
   13803            0 :       free(nc);
   13804            0 :       return RPC_NET_ERROR;
   13805              :    }
   13806              : 
   13807            0 :    free(nc);
   13808            0 :    nc = NULL;
   13809              : 
   13810            0 :    bool restore_watchdog_timeout = false;
   13811              :    BOOL watchdog_call;
   13812              :    DWORD watchdog_timeout;
   13813            0 :    cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
   13814              : 
   13815              :    //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
   13816              : 
   13817            0 :    if (!rpc_is_remote()) {
   13818              :       // if RPC is remote, we are connected to an mserver,
   13819              :       // the mserver takes care of watchdog timeouts.
   13820              :       // otherwise we should make sure the watchdog timeout
   13821              :       // is longer than the RPC timeout. K.O.
   13822            0 :       if (rpc_timeout >= (int) watchdog_timeout) {
   13823            0 :          restore_watchdog_timeout = true;
   13824            0 :          cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
   13825              :       }
   13826              :    }
   13827              : 
   13828            0 :    DWORD rpc_status = 0;
   13829            0 :    DWORD buf_size = 0;
   13830            0 :    char* buf = NULL;
   13831              : 
   13832            0 :    status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
   13833              : 
   13834            0 :    if (restore_watchdog_timeout) {
   13835            0 :       cm_set_watchdog_params_local(watchdog_call, watchdog_timeout);
   13836              :    }
   13837              : 
   13838              :    /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
   13839              : 
   13840            0 :    ss_mutex_release(_mutex_rpc);
   13841              : 
   13842              :    /* check for reply errors */
   13843              : 
   13844            0 :    if (status == SS_TIMEOUT) {
   13845            0 :       cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
   13846            0 :       if (buf)
   13847            0 :          free(buf);
   13848            0 :       abort(); // cannot continue - our mserver is not talking to us!
   13849              :       return RPC_TIMEOUT;
   13850              :    }
   13851              : 
   13852            0 :    if (status != SS_SUCCESS) {
   13853            0 :       cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
   13854            0 :       if (buf)
   13855            0 :          free(buf);
   13856            0 :       abort(); // cannot continue - something is wrong with our mserver connection
   13857              :       return RPC_NET_ERROR;
   13858              :    }
   13859              : 
   13860            0 :    if (rpc_status == RPC_INVALID_ID) {
   13861            0 :       cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
   13862            0 :       if (buf)
   13863            0 :          free(buf);
   13864            0 :       return rpc_status;
   13865              :    }
   13866              : 
   13867              :    /* extract result variables and place it to argument list */
   13868              : 
   13869            0 :    va_start(ap, routine_id);
   13870              : 
   13871            0 :    status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
   13872              : 
   13873            0 :    if (status != RPC_SUCCESS) {
   13874            0 :       rpc_status = status;
   13875              :    }
   13876              : 
   13877            0 :    va_end(ap);
   13878              : 
   13879            0 :    if (buf)
   13880            0 :       free(buf);
   13881              : 
   13882            0 :    return rpc_status;
   13883              : }
   13884              : 
   13885              : 
   13886              : /********************************************************************/
   13887            0 : INT rpc_set_opt_tcp_size(INT tcp_size) {
   13888              :    INT old;
   13889              : 
   13890            0 :    old = _opt_tcp_size;
   13891            0 :    _opt_tcp_size = tcp_size;
   13892            0 :    return old;
   13893              : }
   13894              : 
   13895            0 : INT rpc_get_opt_tcp_size() {
   13896            0 :    return _opt_tcp_size;
   13897              : }
   13898              : 
   13899              : /**dox***************************************************************/
   13900              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   13901              : 
   13902              : /********************************************************************/
   13903              : /**
   13904              : Fast send_event routine which bypasses the RPC layer and
   13905              :            sends the event directly at the TCP level.
   13906              : @param buffer_handle      Handle of the buffer to send the event to.
   13907              :                           Must be obtained via bm_open_buffer.
   13908              : @param source             Address of the event to send. It must have
   13909              :                           a proper event header.
   13910              : @param buf_size           Size of event in bytes with header.
   13911              : @param async_flag         BM_WAIT / BM_NO_WAIT flag. In BM_NO_WAIT mode, the
   13912              :                           function returns immediately if it cannot
   13913              :                           send the event over the network. In BM_WAIT
   13914              :                           mode, it waits until the packet is sent
   13915              :                           (blocking).
   13916              : @param mode               Determines in which mode the event is sent.
   13917              :                           If zero, use RPC socket, if one, use special
   13918              :                           event socket to bypass RPC layer on the
   13919              :                           server side.
   13920              : 
   13921              : @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR,
   13922              :         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER
   13923              : */
   13924            0 : INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
   13925              : {
   13926            0 :    if (rpc_is_remote()) {
   13927            0 :       return rpc_send_event1(buffer_handle, pevent);
   13928              :    } else {
   13929            0 :       return bm_send_event(buffer_handle, pevent, unused, async_flag);
   13930              :    }
   13931              : }
   13932              : 
   13933              : /********************************************************************/
   13934              : /**
   13935              : Send event to mserver using the event socket connection, bypassing the RPC layer
   13936              : @param buffer_handle      Handle of the buffer to send the event to.
   13937              :                           Must be obtained via bm_open_buffer.
   13938              : @param event              Pointer to event header
   13939              : 
   13940              : @return RPC_SUCCESS, RPC_NET_ERROR, RPC_NO_CONNECTION
   13941              : */
   13942            0 : INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
   13943              : {
   13944            0 :    const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
   13945            0 :    return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
   13946              : }
   13947              : 
   13948            0 : INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
   13949              : {
   13950            0 :    if (sg_n < 1) {
   13951            0 :       cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
   13952            0 :       return BM_INVALID_SIZE;
   13953              :    }
   13954              : 
   13955            0 :    if (sg_ptr[0] == NULL) {
   13956            0 :       cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
   13957            0 :       return BM_INVALID_SIZE;
   13958              :    }
   13959              : 
   13960            0 :    if (sg_len[0] < sizeof(EVENT_HEADER)) {
   13961            0 :       cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_len[0] value %d is smaller than event header size %d", (int)sg_len[0], (int)sizeof(EVENT_HEADER));
   13962            0 :       return BM_INVALID_SIZE;
   13963              :    }
   13964              : 
   13965            0 :    const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
   13966              :    
   13967            0 :    const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
   13968            0 :    const DWORD data_size = pevent->data_size; // 32-bit unsigned value
   13969              : 
   13970            0 :    if (data_size == 0) {
   13971            0 :       cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
   13972            0 :       return BM_INVALID_SIZE;
   13973              :    }
   13974              : 
   13975            0 :    if (data_size > MAX_DATA_SIZE) {
   13976            0 :       cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size %d (0x%x) maximum is %d (0x%x)", data_size, data_size, MAX_DATA_SIZE, MAX_DATA_SIZE);
   13977            0 :       return BM_INVALID_SIZE;
   13978              :    }
   13979              : 
   13980            0 :    const size_t event_size = sizeof(EVENT_HEADER) + data_size;
   13981            0 :    const size_t total_size = ALIGN8(event_size);
   13982              : 
   13983            0 :    size_t count = 0;
   13984            0 :    for (int i=0; i<sg_n; i++) {
   13985            0 :       count += sg_len[i];
   13986              :    }
   13987              : 
   13988            0 :    if (count != event_size) {
   13989            0 :       cm_msg(MERROR, "rpc_send_event_sg", "data size mismatch: event data_size %d, event_size %d not same as sum of sg_len %d", (int)data_size, (int)event_size, (int)count);
   13990            0 :       return BM_INVALID_SIZE;
   13991              :    }
   13992              : 
   13993              :    // protect non-atomic access to _server_connection.event_sock. K.O.
   13994              :    
   13995            0 :    std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
   13996              : 
   13997              :    //printf("rpc_send_event_sg: pevent %p, event_id 0x%04x, serial 0x%08x, data_size %d, event_size %d, total_size %d\n", pevent, pevent->event_id, pevent->serial_number, (int)data_size, (int)event_size, (int)total_size);
   13998              : 
   13999            0 :    if (_server_connection.event_sock == 0) {
   14000            0 :       return RPC_NO_CONNECTION;
   14001              :    }
   14002              : 
   14003              :    //
   14004              :    // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
   14005              :    //
   14006              :    // 4 bytes of buffer handle
   14007              :    // 16 bytes of event header, includes data_size
   14008              :    // ALIGN8(data_size) bytes of event data
   14009              :    // 
   14010              : 
   14011              :    int status;
   14012              : 
   14013              :    /* send buffer handle */
   14014              : 
   14015              :    assert(sizeof(DWORD) == 4);
   14016            0 :    DWORD bh_buf = buffer_handle;
   14017              :    
   14018            0 :    status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
   14019            0 :    if (status != SS_SUCCESS) {
   14020            0 :       ss_socket_close(&_server_connection.event_sock);
   14021            0 :       cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
   14022            0 :       return RPC_NET_ERROR;
   14023              :    }
   14024              : 
   14025              :    /* send data */
   14026              : 
   14027            0 :    for (int i=0; i<sg_n; i++) {
   14028            0 :       status = ss_write_tcp(_server_connection.event_sock, sg_ptr[i], sg_len[i]);
   14029            0 :       if (status != SS_SUCCESS) {
   14030            0 :          ss_socket_close(&_server_connection.event_sock);
   14031            0 :          cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
   14032            0 :          return RPC_NET_ERROR;
   14033              :       }
   14034              :    }
   14035              : 
   14036              :    /* send padding */
   14037              : 
   14038            0 :    if (count < total_size) {
   14039            0 :       char padding[8] = { 0,0,0,0,0,0,0,0 };
   14040            0 :       size_t padlen = total_size - count;
   14041            0 :       assert(padlen < 8);
   14042            0 :       status = ss_write_tcp(_server_connection.event_sock, padding, padlen);
   14043            0 :       if (status != SS_SUCCESS) {
   14044            0 :          ss_socket_close(&_server_connection.event_sock);
   14045            0 :          cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
   14046            0 :          return RPC_NET_ERROR;
   14047              :       }
   14048              :    }
   14049              : 
   14050            0 :    return RPC_SUCCESS;
   14051            0 : }
   14052              : 
   14053              : /********************************************************************/
   14054              : /**
   14055              : Send event residing in the TCP cache buffer filled by
   14056              :            rpc_send_event. This routine should be called when a
   14057              :            run is stopped.
   14058              : 
   14059              : @return RPC_SUCCESS, RPC_NET_ERROR
   14060              : */
   14061            0 : INT rpc_flush_event() {
   14062            0 :    return RPC_SUCCESS;
   14063              : }
   14064              : 
   14065              : /********************************************************************/
   14066              : 
   14067              : struct TR_FIFO {
   14068              :    int transition = 0;
   14069              :    int run_number = 0;
   14070              :    time_t trans_time = 0;
   14071              :    int sequence_number = 0;
   14072              : };
   14073              : 
   14074              : static std::mutex _tr_fifo_mutex;
   14075              : static TR_FIFO _tr_fifo[10];
   14076              : static int _tr_fifo_wp = 0;
   14077              : static int _tr_fifo_rp = 0;
   14078              : 
   14079            0 : static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
   14080              : /********************************************************************\
   14081              : 
   14082              :   Routine: rpc_transition_dispatch
   14083              : 
   14084              :   Purpose: Gets called when a transition function was registered and
   14085              :            a transition occured. Internal use only.
   14086              : 
   14087              :   Input:
   14088              :     INT    idx              RPC function ID
   14089              :     void   *prpc_param      RPC parameters
   14090              : 
   14091              :   Output:
   14092              :     none
   14093              : 
   14094              :   Function value:
   14095              :     INT    return value from called user routine
   14096              : 
   14097              : \********************************************************************/
   14098              : {
   14099              :    /* erase error string */
   14100            0 :    *(CSTRING(2)) = 0;
   14101              : 
   14102            0 :    if (idx == RPC_RC_TRANSITION) {
   14103              :       // find registered handler
   14104              :       // NB: this code should match same code in cm_transition_call_direct()
   14105              :       // NB: only use the first handler, this is how MIDAS always worked
   14106              :       // NB: we could run all handlers, but we can return the status and error string of only one of them.
   14107            0 :       _trans_table_mutex.lock();
   14108            0 :       size_t n = _trans_table.size();
   14109            0 :       _trans_table_mutex.unlock();
   14110              :          
   14111            0 :       for (size_t i = 0; i < n; i++) {
   14112            0 :          _trans_table_mutex.lock();
   14113            0 :          TRANS_TABLE tt = _trans_table[i];
   14114            0 :          _trans_table_mutex.unlock();
   14115              :          
   14116            0 :          if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
   14117            0 :             if (tt.func) {
   14118              :                /* execute callback if defined */
   14119            0 :                return tt.func(CINT(1), CSTRING(2));
   14120              :             } else {
   14121            0 :                std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
   14122              :                /* store transition in FIFO */
   14123            0 :                _tr_fifo[_tr_fifo_wp].transition = CINT(0);
   14124            0 :                _tr_fifo[_tr_fifo_wp].run_number = CINT(1);
   14125            0 :                _tr_fifo[_tr_fifo_wp].trans_time = time(NULL);
   14126            0 :                _tr_fifo[_tr_fifo_wp].sequence_number = CINT(4);
   14127            0 :                _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
   14128              :                // implicit unlock
   14129            0 :                return RPC_SUCCESS;
   14130            0 :             }
   14131              :          }
   14132              :       }
   14133              :       // no handler for this transition
   14134            0 :       cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
   14135            0 :       return CM_SUCCESS;
   14136              :    } else {
   14137            0 :       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
   14138            0 :       return RPC_INVALID_ID;
   14139              :    }
   14140              : }
   14141              : 
   14142              : /********************************************************************/
   14143            0 : int cm_query_transition(int *transition, int *run_number, int *trans_time)
   14144              : /********************************************************************\
   14145              : 
   14146              :   Routine: cm_query_transition
   14147              : 
   14148              :   Purpose: Query system if transition has occured. Normally, one
   14149              :            registers callbacks for transitions via
   14150              :            cm_register_transition. In some environments however,
   14151              :            callbacks are not possible. In that case one spciefies
   14152              :            a NULL pointer as the callback routine and can query
   14153              :            transitions "manually" by calling this functions. A small
   14154              :            FIFO takes care that no transition is lost if this functions
   14155              :            did not get called between some transitions.
   14156              : 
   14157              :   Output:
   14158              :     INT   *transition        Type of transition, one of TR_xxx
   14159              :     INT   *run_nuber         Run number for transition
   14160              :     time_t *trans_time       Time (in UNIX time) of transition
   14161              : 
   14162              :   Function value:
   14163              :     FALSE  No transition occured since last call
   14164              :     TRUE   Transition occured
   14165              : 
   14166              : \********************************************************************/
   14167              : {
   14168            0 :    std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
   14169              : 
   14170            0 :    if (_tr_fifo_wp == _tr_fifo_rp)
   14171            0 :       return FALSE;
   14172              : 
   14173            0 :    if (transition)
   14174            0 :       *transition = _tr_fifo[_tr_fifo_rp].transition;
   14175              : 
   14176            0 :    if (run_number)
   14177            0 :       *run_number = _tr_fifo[_tr_fifo_rp].run_number;
   14178              : 
   14179            0 :    if (trans_time)
   14180            0 :       *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
   14181              : 
   14182            0 :    _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
   14183              : 
   14184              :    // implicit unlock
   14185            0 :    return TRUE;
   14186            0 : }
   14187              : 
   14188              : /********************************************************************\
   14189              : *                        server functions                            *
   14190              : \********************************************************************/
   14191              : 
   14192              : #if 0
   14193              : void debug_dump(unsigned char *p, int size)
   14194              : {
   14195              :    int i, j;
   14196              :    unsigned char c;
   14197              : 
   14198              :    for (i = 0; i < (size - 1) / 16 + 1; i++) {
   14199              :       printf("%p ", p + i * 16);
   14200              :       for (j = 0; j < 16; j++)
   14201              :          if (i * 16 + j < size)
   14202              :             printf("%02X ", p[i * 16 + j]);
   14203              :          else
   14204              :             printf("   ");
   14205              :       printf(" ");
   14206              : 
   14207              :       for (j = 0; j < 16; j++) {
   14208              :          c = p[i * 16 + j];
   14209              :          if (i * 16 + j < size)
   14210              :             printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
   14211              :       }
   14212              :       printf("\n");
   14213              :    }
   14214              : 
   14215              :    printf("\n");
   14216              : }
   14217              : #endif
   14218              : 
   14219              : /********************************************************************/
   14220            0 : static int recv_net_command_realloc(INT idx, char **pbuf, int *pbufsize, INT *remaining)
   14221              : /********************************************************************\
   14222              : 
   14223              :   Routine: recv_net_command
   14224              : 
   14225              :   Purpose: TCP receive routine with local cache. To speed up network
   14226              :            performance, a 64k buffer is read in at once and split into
   14227              :            several RPC command on successive calls to recv_net_command.
   14228              :            Therefore, the number of recv() calls is minimized.
   14229              : 
   14230              :            This routine is ment to be called by the server process.
   14231              :            Clients should call recv_tcp instead.
   14232              : 
   14233              :   Input:
   14234              :     INT   idx                Index of server connection
   14235              :     DWORD buffer_size        Size of the buffer in bytes.
   14236              :     INT   flags              Flags passed to recv()
   14237              :     INT   convert_flags      Convert flags needed for big/little
   14238              :                              endian conversion
   14239              : 
   14240              :   Output:
   14241              :     char  *buffer            Network receive buffer.
   14242              :     INT   *remaining         Remaining data in cache
   14243              : 
   14244              :   Function value:
   14245              :     INT                      Same as recv()
   14246              : 
   14247              : \********************************************************************/
   14248              : {
   14249            0 :    char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
   14250              : 
   14251            0 :    RPC_SERVER_ACCEPTION* sa = rpc_get_server_acception(idx);
   14252              : 
   14253            0 :    int sock = sa->recv_sock;
   14254              : 
   14255            0 :    if (!sa->net_buffer) {
   14256            0 :       if (sa->is_mserver)
   14257            0 :          sa->net_buffer_size = NET_TCP_SIZE;
   14258              :       else
   14259            0 :          sa->net_buffer_size = NET_BUFFER_SIZE;
   14260              : 
   14261            0 :       sa->net_buffer = (char *) malloc(sa->net_buffer_size);
   14262              :       //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
   14263            0 :       sa->write_ptr = 0;
   14264            0 :       sa->read_ptr = 0;
   14265            0 :       sa->misalign = 0;
   14266              :    }
   14267            0 :    if (!sa->net_buffer) {
   14268            0 :       cm_msg(MERROR, "recv_net_command", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
   14269            0 :       return -1;
   14270              :    }
   14271              : 
   14272            0 :    int copied = 0;
   14273            0 :    int param_size = -1;
   14274              : 
   14275            0 :    int write_ptr = sa->write_ptr;
   14276            0 :    int read_ptr = sa->read_ptr;
   14277            0 :    int misalign = sa->misalign;
   14278            0 :    char *net_buffer = sa->net_buffer;
   14279              : 
   14280              :    do {
   14281            0 :       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
   14282            0 :          if (param_size == -1) {
   14283            0 :             if (copied > 0) {
   14284              :                /* assemble split header */
   14285            0 :                memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
   14286            0 :                NET_COMMAND *nc = (NET_COMMAND *) (buffer);
   14287            0 :                param_size = (INT) nc->header.param_size;
   14288              :             } else {
   14289            0 :                NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
   14290            0 :                param_size = (INT) nc->header.param_size;
   14291              :             }
   14292              : 
   14293            0 :             if (sa->convert_flags)
   14294            0 :                rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
   14295              :          }
   14296              : 
   14297              :          //printf("recv_net_command: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
   14298              : 
   14299              :          /* check if parameters fit in buffer */
   14300            0 :          if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
   14301            0 :             int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
   14302            0 :             char *p = (char *) realloc(*pbuf, new_size);
   14303              :             //printf("recv_net_command: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
   14304            0 :             if (p == NULL) {
   14305            0 :                cm_msg(MERROR, "recv_net_command", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
   14306            0 :                sa->read_ptr = 0;
   14307            0 :                sa->write_ptr = 0;
   14308            0 :                return -1;
   14309              :             }
   14310            0 :             *pbuf = p;
   14311            0 :             *pbufsize = new_size;
   14312              :          }
   14313              : 
   14314            0 :          buffer = *pbuf;
   14315              : 
   14316              :          /* check if we have all parameters in buffer */
   14317            0 :          if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
   14318            0 :             break;
   14319              :       }
   14320              : 
   14321              :       /* not enough data, so copy partially and get new */
   14322            0 :       int size = write_ptr - read_ptr;
   14323              : 
   14324            0 :       if (size > 0) {
   14325            0 :          memcpy(buffer + copied, net_buffer + read_ptr, size);
   14326            0 :          copied += size;
   14327            0 :          read_ptr = write_ptr;
   14328              :       }
   14329              : #ifdef OS_UNIX
   14330              :       do {
   14331            0 :          write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
   14332              : 
   14333              :          /* don't return if an alarm signal was cought */
   14334            0 :       } while (write_ptr == -1 && errno == EINTR);
   14335              : #else
   14336              :       write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
   14337              : #endif
   14338              : 
   14339              :       /* abort if connection broken */
   14340            0 :       if (write_ptr <= 0) {
   14341            0 :          if (write_ptr == 0)
   14342            0 :             cm_msg(MERROR, "recv_net_command", "rpc connection from \'%s\' on \'%s\' unexpectedly closed", sa->prog_name.c_str(), sa->host_name.c_str());
   14343              :          else
   14344            0 :             cm_msg(MERROR, "recv_net_command", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
   14345              : 
   14346            0 :          if (remaining)
   14347            0 :             *remaining = 0;
   14348              : 
   14349            0 :          return write_ptr;
   14350              :       }
   14351              : 
   14352            0 :       read_ptr = misalign;
   14353            0 :       write_ptr += misalign;
   14354              : 
   14355            0 :       misalign = write_ptr % 8;
   14356            0 :    } while (TRUE);
   14357              : 
   14358              :    /* copy rest of parameters */
   14359            0 :    int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
   14360            0 :    memcpy(buffer + copied, net_buffer + read_ptr, size);
   14361            0 :    read_ptr += size;
   14362              : 
   14363            0 :    if (remaining) {
   14364              :       /* don't keep rpc_server_receive in an infinite loop */
   14365            0 :       if (write_ptr - read_ptr < param_size)
   14366            0 :          *remaining = 0;
   14367              :       else
   14368            0 :          *remaining = write_ptr - read_ptr;
   14369              :    }
   14370              : 
   14371            0 :    sa->write_ptr = write_ptr;
   14372            0 :    sa->read_ptr = read_ptr;
   14373            0 :    sa->misalign = misalign;
   14374              : 
   14375            0 :    return size + copied;
   14376              : }
   14377              : 
   14378              : 
   14379              : /********************************************************************/
   14380            0 : INT recv_tcp_check(int sock)
   14381              : /********************************************************************\
   14382              : 
   14383              :   Routine: recv_tcp_check
   14384              : 
   14385              :   Purpose: Check if in TCP receive buffer associated with sock is
   14386              :            some data. Called by ss_suspend.
   14387              : 
   14388              :   Input:
   14389              :     INT   sock               TCP receive socket
   14390              : 
   14391              :   Output:
   14392              :     none
   14393              : 
   14394              :   Function value:
   14395              :     INT   count              Number of bytes remaining in TCP buffer
   14396              : 
   14397              : \********************************************************************/
   14398              : {
   14399              :    /* figure out to which connection socket belongs */
   14400            0 :    for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
   14401            0 :       if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
   14402            0 :          return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
   14403              :       }
   14404              : 
   14405            0 :    return 0;
   14406              : }
   14407              : 
   14408              : 
   14409              : /********************************************************************/
   14410            0 : static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION* psa, char **pbuffer, int *pbuffer_size)
   14411              : /********************************************************************\
   14412              : 
   14413              :   Routine: recv_event_server_realloc
   14414              : 
   14415              :   Purpose: receive events sent by rpc_send_event()
   14416              : 
   14417              :   Input:
   14418              :     INT   idx                Index of server connection
   14419              :     DWORD buffer_size        Size of the buffer in bytes.
   14420              :     INT   flags              Flags passed to recv()
   14421              :     INT   convert_flags      Convert flags needed for big/little
   14422              :                              endian conversion
   14423              : 
   14424              :   Output:
   14425              :     char  *buffer            Network receive buffer.
   14426              :     INT   *remaining         Remaining data in cache
   14427              : 
   14428              :   Function value:
   14429              :     INT                      Same as recv()
   14430              : 
   14431              : \********************************************************************/
   14432              : {
   14433            0 :    int sock = psa->event_sock;
   14434              : 
   14435              :    //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
   14436              : 
   14437            0 :    const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
   14438              : 
   14439              :    char header_buf[header_size];
   14440              : 
   14441              :    // First read the header.
   14442              :    //
   14443              :    // Data format is:
   14444              :    // INT buffer handle (4 bytes)
   14445              :    // EVENT_HEADER (16 bytes)
   14446              :    // event data
   14447              :    // ALIGN8() padding
   14448              :    // ...next event
   14449              : 
   14450            0 :    int hrd = recv_tcp2(sock, header_buf, header_size, 1);
   14451              : 
   14452            0 :    if (hrd == 0) {
   14453              :       // timeout waiting for data
   14454            0 :       return 0;
   14455              :    }
   14456              : 
   14457              :    /* abort if connection broken */
   14458            0 :    if (hrd < 0) {
   14459            0 :       cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
   14460            0 :       return -1;
   14461              :    }
   14462              : 
   14463            0 :    if (hrd < (int) header_size) {
   14464            0 :       int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
   14465              : 
   14466              :       /* abort if connection broken */
   14467            0 :       if (hrd1 <= 0) {
   14468            0 :          cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
   14469            0 :          return -1;
   14470              :       }
   14471              : 
   14472            0 :       hrd += hrd1;
   14473              :    }
   14474              : 
   14475              :    /* abort if connection broken */
   14476            0 :    if (hrd != (int) header_size) {
   14477            0 :       cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
   14478            0 :       return -1;
   14479              :    }
   14480              : 
   14481            0 :    INT *pbh = (INT *) header_buf;
   14482            0 :    EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
   14483              : 
   14484              :    /* convert header little endian/big endian */
   14485            0 :    if (psa->convert_flags) {
   14486            0 :       rpc_convert_single(&pbh, TID_INT32, 0, psa->convert_flags);
   14487            0 :       rpc_convert_single(&pevent->event_id, TID_INT16, 0, psa->convert_flags);
   14488            0 :       rpc_convert_single(&pevent->trigger_mask, TID_INT16, 0, psa->convert_flags);
   14489            0 :       rpc_convert_single(&pevent->serial_number, TID_UINT32, 0, psa->convert_flags);
   14490            0 :       rpc_convert_single(&pevent->time_stamp, TID_UINT32, 0, psa->convert_flags);
   14491            0 :       rpc_convert_single(&pevent->data_size, TID_UINT32, 0, psa->convert_flags);
   14492              :    }
   14493              : 
   14494            0 :    int event_size = pevent->data_size + sizeof(EVENT_HEADER);
   14495            0 :    int total_size = ALIGN8(event_size);
   14496              : 
   14497              :    //printf("recv_event_server: buffer_handle %d, event_id 0x%04x, serial 0x%08x, data_size %d, event_size %d, total_size %d\n", *pbh, pevent->event_id, pevent->serial_number, pevent->data_size, event_size, total_size);
   14498              : 
   14499            0 :    if (pevent->data_size == 0) {
   14500            0 :       for (int i=0; i<5; i++) {
   14501            0 :          printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
   14502              :       }
   14503            0 :       abort();
   14504              :    }
   14505              : 
   14506              :    /* check for sane event size */
   14507            0 :    if (event_size <= 0 || total_size <= 0) {
   14508            0 :       cm_msg(MERROR, "recv_event_server",
   14509              :              "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
   14510              :              event_size, total_size);
   14511            0 :       return -1;
   14512              :    }
   14513              : 
   14514              :    //printf("recv_event_server: idx %d, bh %d, event header: id %d, mask %d, serial %d, data_size %d, event_size %d, total_size %d\n", idx, *pbh, pevent->event_id, pevent->trigger_mask, pevent->serial_number, pevent->data_size, event_size, total_size);
   14515              : 
   14516              : 
   14517            0 :    int bufsize = sizeof(INT) + total_size;
   14518              : 
   14519              :    // Second, check that output buffer is big enough
   14520              : 
   14521              :    /* check if data part fits in buffer */
   14522            0 :    if (*pbuffer_size < bufsize) {
   14523            0 :       int newsize = 1024 + ALIGN8(bufsize);
   14524              : 
   14525              :       //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
   14526              : 
   14527            0 :       char *newbuf = (char *) realloc(*pbuffer, newsize);
   14528            0 :       if (newbuf == NULL) {
   14529            0 :          cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
   14530              :                 newsize);
   14531            0 :          return -1;
   14532              :       }
   14533            0 :       *pbuffer = newbuf;
   14534            0 :       *pbuffer_size = newsize;
   14535              :    }
   14536              : 
   14537              :    // Third, copy header into output buffer
   14538              : 
   14539            0 :    memcpy(*pbuffer, header_buf, header_size);
   14540              : 
   14541              :    // Forth, read the event data
   14542              : 
   14543            0 :    int to_read = sizeof(INT) + total_size - header_size;
   14544            0 :    int rptr = header_size;
   14545              : 
   14546            0 :    if (to_read > 0) {
   14547            0 :       int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
   14548              : 
   14549              :       /* abort if connection broken */
   14550            0 :       if (drd <= 0) {
   14551            0 :          cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
   14552            0 :          return -1;
   14553              :       }
   14554              :    }
   14555              : 
   14556            0 :    return bufsize;
   14557              : }
   14558              : 
   14559              : 
   14560              : /********************************************************************/
   14561            2 : INT rpc_register_server(int port, int *plsock, int *pport)
   14562              : /********************************************************************\
   14563              : 
   14564              :   Routine: rpc_register_listener
   14565              : 
   14566              :   Purpose: Register the calling process as a MIDAS RPC server. Note
   14567              :            that cm_connnect_experiment must be called prior to any call of
   14568              :            rpc_register_server.
   14569              : 
   14570              :   Input:
   14571              :     INT   port              TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
   14572              : 
   14573              :   Output:
   14574              :     int   *plsock           Listener socket, can be NULL
   14575              :     int   *pport            Port under which server is listening, can be NULL
   14576              : 
   14577              :   Function value:
   14578              :     RPC_SUCCESS             Successful completion
   14579              :     RPC_NET_ERROR           Error in socket call
   14580              :     RPC_NOT_REGISTERED      cm_connect_experiment was not called
   14581              : 
   14582              : \********************************************************************/
   14583              : {
   14584              :    int status;
   14585              :    int lsock;
   14586              : 
   14587            2 :    status = rpc_register_listener(port, NULL, &lsock, pport);
   14588            2 :    if (status != RPC_SUCCESS)
   14589            0 :       return status;
   14590              : 
   14591            2 :    status = ss_suspend_set_client_listener(lsock);
   14592            2 :    if (status != SS_SUCCESS)
   14593            0 :       return status;
   14594              : 
   14595            2 :    if (plsock)
   14596            2 :       *plsock = lsock;
   14597              : 
   14598            2 :    return RPC_SUCCESS;
   14599              : }
   14600              : 
   14601              : /********************************************************************/
   14602            2 : INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
   14603              : /********************************************************************\
   14604              : 
   14605              :   Routine: rpc_register_listener
   14606              : 
   14607              :   Purpose: Register the calling process as a MIDAS RPC server. Note
   14608              :            that cm_connnect_experiment must be called prior to any call of
   14609              :            rpc_register_listener.
   14610              : 
   14611              :   Input:
   14612              :     INT   port              TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
   14613              :     INT   *func             Default dispatch function
   14614              : 
   14615              :   Output:
   14616              :     int   *plsock           Listener socket, should not be NULL
   14617              :     int   *pport            Port under which server is listening, can be NULL
   14618              : 
   14619              :   Function value:
   14620              :     RPC_SUCCESS             Successful completion
   14621              :     RPC_NET_ERROR           Error in socket call
   14622              :     RPC_NOT_REGISTERED      cm_connect_experiment was not called
   14623              : 
   14624              : \********************************************************************/
   14625              : {
   14626              :    /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
   14627            2 :    rpc_register_functions(rpc_get_internal_list(0), func);
   14628              : 
   14629              :    /* create a socket for listening */
   14630            2 :    int lsock = 0;
   14631            2 :    int lport = 0;
   14632            2 :    std::string errmsg;
   14633              : 
   14634            2 :    int status = ss_socket_listen_tcp(!disable_bind_rpc_to_localhost, port, &lsock, &lport, &errmsg);
   14635              : 
   14636            2 :    if (status != SS_SUCCESS) {
   14637            0 :       cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
   14638            0 :       return RPC_NET_ERROR;
   14639              :    }
   14640              : 
   14641              :    /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
   14642              : #if defined(F_SETFD) && defined(FD_CLOEXEC)
   14643            2 :    status = fcntl(lsock, F_SETFD, fcntl(lsock, F_GETFD) | FD_CLOEXEC);
   14644            2 :    if (status < 0) {
   14645            0 :       cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
   14646            0 :       return RPC_NET_ERROR;
   14647              :    }
   14648              : #endif
   14649              : 
   14650              :    /* return port wich OS has choosen */
   14651            2 :    if (pport) {
   14652            2 :       *pport = lport;
   14653              :    }
   14654              : 
   14655            2 :    if (plsock)
   14656            2 :       *plsock = lsock;
   14657              : 
   14658              :    //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
   14659              : 
   14660            2 :    return RPC_SUCCESS;
   14661            2 : }
   14662              : 
   14663              : typedef struct {
   14664              :    midas_thread_t thread_id;
   14665              :    int buffer_size;
   14666              :    char *buffer;
   14667              : } TLS_POINTER;
   14668              : 
   14669              : static TLS_POINTER *tls_buffer = NULL;
   14670              : static int tls_size = 0;
   14671              : 
   14672              : /********************************************************************/
   14673            0 : INT rpc_execute(INT sock, char *buffer, INT convert_flags)
   14674              : /********************************************************************\
   14675              : 
   14676              :   Routine: rpc_execute
   14677              : 
   14678              :   Purpose: Execute a RPC command received over the network
   14679              : 
   14680              :   Input:
   14681              :     INT  sock               TCP socket to which the result should be
   14682              :                             send back
   14683              : 
   14684              :     char *buffer            Command buffer
   14685              :     INT  convert_flags      Flags for data conversion
   14686              : 
   14687              :   Output:
   14688              :     none
   14689              : 
   14690              :   Function value:
   14691              :     RPC_SUCCESS             Successful completion
   14692              :     RPC_INVALID_ID          Invalid routine_id received
   14693              :     RPC_NET_ERROR           Error in socket call
   14694              :     RPC_EXCEED_BUFFER       Not enough memory for network buffer
   14695              :     RPC_SHUTDOWN            Shutdown requested
   14696              :     SS_ABORT                TCP connection broken
   14697              :     SS_EXIT                 TCP connection closed
   14698              : 
   14699              : \********************************************************************/
   14700              : {
   14701              :    INT i, routine_id, status;
   14702              :    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
   14703              :    INT tid, flags;
   14704              :    NET_COMMAND *nc_in, *nc_out;
   14705              :    INT param_size, max_size;
   14706              :    void *prpc_param[20];
   14707              :    char debug_line[1024], *return_buffer;
   14708              :    int return_buffer_size;
   14709              :    int return_buffer_tls;
   14710              : #ifdef FIXED_BUFFER
   14711              :    int initial_buffer_size = NET_BUFFER_SIZE;
   14712              : #else
   14713            0 :    int initial_buffer_size = 1024;
   14714              : #endif
   14715              : 
   14716              :    /* return buffer must must use thread local storage multi-thread servers */
   14717            0 :    if (!tls_size) {
   14718            0 :       tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
   14719            0 :       tls_buffer[tls_size].thread_id = ss_gettid();
   14720            0 :       tls_buffer[tls_size].buffer_size = initial_buffer_size;
   14721            0 :       tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
   14722            0 :       tls_size = 1;
   14723              :    }
   14724            0 :    for (i = 0; i < tls_size; i++)
   14725            0 :       if (tls_buffer[i].thread_id == ss_gettid())
   14726            0 :          break;
   14727            0 :    if (i == tls_size) {
   14728              :       /* new thread -> allocate new buffer */
   14729            0 :       tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size + 1) * sizeof(TLS_POINTER));
   14730            0 :       tls_buffer[tls_size].thread_id = ss_gettid();
   14731            0 :       tls_buffer[tls_size].buffer_size = initial_buffer_size;
   14732            0 :       tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
   14733            0 :       tls_size++;
   14734              :    }
   14735              : 
   14736            0 :    return_buffer_tls = i;
   14737            0 :    return_buffer_size = tls_buffer[i].buffer_size;
   14738            0 :    return_buffer = tls_buffer[i].buffer;
   14739            0 :    assert(return_buffer);
   14740              : 
   14741              :    // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
   14742              :    // and valgrind complains that we transmit uninitialized data
   14743              :    //memset(return_buffer, 0, return_buffer_size);
   14744              : 
   14745              :    /* extract pointer array to parameters */
   14746            0 :    nc_in = (NET_COMMAND *) buffer;
   14747              : 
   14748              :    /* convert header format (byte swapping) */
   14749            0 :    if (convert_flags) {
   14750            0 :       rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
   14751            0 :       rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
   14752              :    }
   14753              : 
   14754              :    //if (nc_in->header.routine_id & RPC_NO_REPLY) {
   14755              :    //   printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
   14756              :    //}
   14757              : 
   14758              :    /* no result return as requested */
   14759            0 :    if (nc_in->header.routine_id & RPC_NO_REPLY)
   14760            0 :       sock = 0;
   14761              : 
   14762              :    /* find entry in rpc_list */
   14763            0 :    routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
   14764              : 
   14765            0 :    int idx = -1;
   14766              :    RPC_LIST rl;
   14767              : 
   14768            0 :    rpc_list_mutex.lock();
   14769              : 
   14770            0 :    for (size_t i = 0; i < rpc_list.size(); i++) {
   14771            0 :       if (rpc_list[i].id == routine_id) {
   14772            0 :          idx = i;
   14773            0 :          rl = rpc_list[idx];
   14774            0 :          break;
   14775              :       }
   14776              :    }
   14777              : 
   14778            0 :    rpc_list_mutex.unlock();
   14779              : 
   14780            0 :    if (idx < 0) {
   14781            0 :       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
   14782            0 :       return RPC_INVALID_ID;
   14783              :    }
   14784              : 
   14785            0 :    again:
   14786              : 
   14787            0 :    in_param_ptr = nc_in->param;
   14788              : 
   14789            0 :    nc_out = (NET_COMMAND *) return_buffer;
   14790            0 :    out_param_ptr = nc_out->param;
   14791              : 
   14792            0 :    sprintf(debug_line, "%s(", rl.name);
   14793              : 
   14794            0 :    for (i = 0; rl.param[i].tid != 0; i++) {
   14795            0 :       tid = rl.param[i].tid;
   14796            0 :       flags = rl.param[i].flags;
   14797              : 
   14798            0 :       if (flags & RPC_IN) {
   14799            0 :          param_size = ALIGN8(rpc_tid_size(tid));
   14800              : 
   14801            0 :          if (tid == TID_STRING || tid == TID_LINK)
   14802            0 :             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
   14803              : 
   14804            0 :          if (flags & RPC_VARARRAY) {
   14805              :             /* for arrays, the size is stored as a INT in front of the array */
   14806            0 :             param_size = *((INT *) in_param_ptr);
   14807            0 :             if (convert_flags)
   14808            0 :                rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
   14809            0 :             param_size = ALIGN8(param_size);
   14810              : 
   14811            0 :             in_param_ptr += ALIGN8(sizeof(INT));
   14812              :          }
   14813              : 
   14814            0 :          if (tid == TID_STRUCT)
   14815            0 :             param_size = ALIGN8(rl.param[i].n);
   14816              : 
   14817            0 :          prpc_param[i] = in_param_ptr;
   14818              : 
   14819              :          /* convert data format */
   14820            0 :          if (convert_flags) {
   14821            0 :             if (flags & RPC_VARARRAY)
   14822            0 :                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
   14823              :             else
   14824            0 :                rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
   14825              :                                 convert_flags);
   14826              :          }
   14827              : 
   14828            0 :          std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
   14829            0 :          if (rl.param[i].tid == TID_STRING) {
   14830              :             /* check for long strings (db_create_record...) */
   14831            0 :             if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
   14832            0 :                strcat(debug_line, "\"");
   14833            0 :                strcat(debug_line, str.c_str());
   14834            0 :                strcat(debug_line, "\"");
   14835              :             } else
   14836            0 :                strcat(debug_line, "...");
   14837              :          } else
   14838            0 :             strcat(debug_line, str.c_str());
   14839              : 
   14840            0 :          in_param_ptr += param_size;
   14841            0 :       }
   14842              : 
   14843            0 :       if (flags & RPC_OUT) {
   14844            0 :          param_size = ALIGN8(rpc_tid_size(tid));
   14845              : 
   14846            0 :          if (flags & RPC_VARARRAY || tid == TID_STRING) {
   14847              : 
   14848              :             /* save maximum array length from the value of the next argument.
   14849              :              * this means RPC_OUT arrays and strings should always be passed like this:
   14850              :              * rpc_call(..., array_ptr, array_max_size, ...); */
   14851              : 
   14852            0 :             max_size = *((INT *) in_param_ptr);
   14853              : 
   14854            0 :             if (convert_flags)
   14855            0 :                rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
   14856            0 :             max_size = ALIGN8(max_size);
   14857              : 
   14858            0 :             *((INT *) out_param_ptr) = max_size;
   14859              : 
   14860              :             /* save space for return array length */
   14861            0 :             out_param_ptr += ALIGN8(sizeof(INT));
   14862              : 
   14863              :             /* use maximum array length from input */
   14864            0 :             param_size = max_size;
   14865              :          }
   14866              : 
   14867            0 :          if (rl.param[i].tid == TID_STRUCT)
   14868            0 :             param_size = ALIGN8(rl.param[i].n);
   14869              : 
   14870            0 :          if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
   14871              : #ifdef FIXED_BUFFER
   14872              :             cm_msg(MERROR, "rpc_execute",
   14873              :                    "return parameters (%d) too large for network buffer (%d)",
   14874              :                    (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size);
   14875              : 
   14876              :             return RPC_EXCEED_BUFFER;
   14877              : #else
   14878              :             int itls;
   14879            0 :             int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
   14880              : 
   14881              : #if 0
   14882              :             cm_msg(MINFO, "rpc_execute",
   14883              :                       "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
   14884              :                       (int)((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size), return_buffer_size, new_size);
   14885              : #endif
   14886              : 
   14887            0 :             itls = return_buffer_tls;
   14888              : 
   14889            0 :             tls_buffer[itls].buffer_size = new_size;
   14890            0 :             tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
   14891              : 
   14892            0 :             if (!tls_buffer[itls].buffer) {
   14893            0 :                cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
   14894            0 :                return RPC_EXCEED_BUFFER;
   14895              :             }
   14896              : 
   14897            0 :             return_buffer_size = tls_buffer[itls].buffer_size;
   14898            0 :             return_buffer = tls_buffer[itls].buffer;
   14899            0 :             assert(return_buffer);
   14900              : 
   14901            0 :             goto again;
   14902              : #endif
   14903              :          }
   14904              : 
   14905              :          /* if parameter goes both directions, copy input to output */
   14906            0 :          if (rl.param[i].flags & RPC_IN)
   14907            0 :             memcpy(out_param_ptr, prpc_param[i], param_size);
   14908              : 
   14909            0 :          if (_debug_print && !(flags & RPC_IN))
   14910            0 :             strcat(debug_line, "-");
   14911              : 
   14912            0 :          prpc_param[i] = out_param_ptr;
   14913            0 :          out_param_ptr += param_size;
   14914              :       }
   14915              : 
   14916            0 :       if (rl.param[i + 1].tid)
   14917            0 :          strcat(debug_line, ", ");
   14918              :    }
   14919              : 
   14920              :    //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
   14921              : 
   14922            0 :    strcat(debug_line, ")");
   14923            0 :    rpc_debug_printf(debug_line);
   14924              : 
   14925            0 :    last_param_ptr = out_param_ptr;
   14926              : 
   14927              :    /*********************************\
   14928              :    *   call dispatch function        *
   14929              :    \*********************************/
   14930            0 :    if (rl.dispatch)
   14931            0 :       status = rl.dispatch(routine_id, prpc_param);
   14932              :    else
   14933            0 :       status = RPC_INVALID_ID;
   14934              : 
   14935            0 :    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
   14936            0 :       status = RPC_SUCCESS;
   14937              : 
   14938              :    /* return immediately for closed down client connections */
   14939            0 :    if (!sock && routine_id == RPC_ID_EXIT)
   14940            0 :       return SS_EXIT;
   14941              : 
   14942            0 :    if (!sock && routine_id == RPC_ID_SHUTDOWN)
   14943            0 :       return RPC_SHUTDOWN;
   14944              : 
   14945              :    /* Return if TCP connection broken */
   14946            0 :    if (status == SS_ABORT)
   14947            0 :       return SS_ABORT;
   14948              : 
   14949              :    /* if sock == 0, we are in FTCP mode and may not sent results */
   14950            0 :    if (!sock)
   14951            0 :       return RPC_SUCCESS;
   14952              : 
   14953              :    /* compress variable length arrays */
   14954            0 :    out_param_ptr = nc_out->param;
   14955            0 :    for (i = 0; rl.param[i].tid != 0; i++)
   14956            0 :       if (rl.param[i].flags & RPC_OUT) {
   14957            0 :          tid = rl.param[i].tid;
   14958            0 :          flags = rl.param[i].flags;
   14959            0 :          param_size = ALIGN8(rpc_tid_size(tid));
   14960              : 
   14961            0 :          if (tid == TID_STRING) {
   14962            0 :             max_size = *((INT *) out_param_ptr);
   14963              :             // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
   14964              :             // and prpc_param() is now pointing to the wrong place. here we know our string data
   14965              :             // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
   14966              :             //const char* param_ptr = (char *) prpc_param[i];
   14967            0 :             const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
   14968              :             //printf("string param [%s] max_size %d\n", param_ptr, max_size);
   14969            0 :             param_size = strlen(param_ptr) + 1;
   14970            0 :             param_size = ALIGN8(param_size);
   14971              : 
   14972              :             /* move string ALIGN8(sizeof(INT)) left */
   14973            0 :             memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
   14974              : 
   14975              :             /* move remaining parameters to end of string */
   14976            0 :             memmove(out_param_ptr + param_size,
   14977            0 :                     out_param_ptr + max_size + ALIGN8(sizeof(INT)),
   14978            0 :                     (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
   14979              :          }
   14980              : 
   14981            0 :          if (flags & RPC_VARARRAY) {
   14982              :             /* store array length at current out_param_ptr */
   14983            0 :             max_size = *((INT *) out_param_ptr);
   14984              :             // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
   14985              :             // and prpc_param() is now pointing to the wrong place. instead, compute location
   14986              :             // of next parameter using max_size. K.O.
   14987              :             // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
   14988            0 :             if (rl.param[i+1].flags & RPC_OUT)
   14989            0 :                param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
   14990              :             else
   14991            0 :                param_size = *((INT *) prpc_param[i + 1]);
   14992            0 :             *((INT *) out_param_ptr) = param_size;      // store new array size
   14993            0 :             if (convert_flags)
   14994            0 :                rpc_convert_single(out_param_ptr, TID_INT32, RPC_OUTGOING, convert_flags);
   14995              : 
   14996            0 :             out_param_ptr += ALIGN8(sizeof(INT));       // step over array size
   14997              : 
   14998            0 :             param_size = ALIGN8(param_size);
   14999              : 
   15000              :             /* move remaining parameters to end of array */
   15001            0 :             memmove(out_param_ptr + param_size,
   15002            0 :                     out_param_ptr + max_size,
   15003            0 :                     (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
   15004              :          }
   15005              : 
   15006            0 :          if (tid == TID_STRUCT)
   15007            0 :             param_size = ALIGN8(rl.param[i].n);
   15008              : 
   15009              :          /* convert data format */
   15010            0 :          if (convert_flags) {
   15011            0 :             if (flags & RPC_VARARRAY)
   15012            0 :                rpc_convert_data(out_param_ptr, tid,
   15013            0 :                                 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
   15014              :             else
   15015            0 :                rpc_convert_data(out_param_ptr, tid,
   15016            0 :                                 rl.param[i].flags | RPC_OUTGOING,
   15017            0 :                                 rl.param[i].n * rpc_tid_size(tid), convert_flags);
   15018              :          }
   15019              : 
   15020            0 :          out_param_ptr += param_size;
   15021              :       }
   15022              : 
   15023              :    /* send return parameters */
   15024            0 :    param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
   15025            0 :    nc_out->header.routine_id = status;
   15026            0 :    nc_out->header.param_size = param_size;
   15027              : 
   15028              :    //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
   15029              : 
   15030              :    /* convert header format (byte swapping) if necessary */
   15031            0 :    if (convert_flags) {
   15032            0 :       rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
   15033            0 :       rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
   15034              :    }
   15035              : 
   15036              :    // valgrind complains about sending uninitialized data, if you care about this, uncomment
   15037              :    // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
   15038              : 
   15039            0 :    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
   15040              : 
   15041            0 :    if (status < 0) {
   15042            0 :       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
   15043            0 :       return RPC_NET_ERROR;
   15044              :    }
   15045              : 
   15046              :    /* print return buffer */
   15047              : /*
   15048              :   printf("Return buffer, ID %d:\n", routine_id);
   15049              :   for (i=0; i<param_size ; i++)
   15050              :     {
   15051              :     status = (char) nc_out->param[i];
   15052              :     printf("%02X ", status);
   15053              :     if (i%8 == 7)
   15054              :       printf("\n");
   15055              :     }
   15056              : */
   15057              :    /* return SS_EXIT if RPC_EXIT is called */
   15058            0 :    if (routine_id == RPC_ID_EXIT)
   15059            0 :       return SS_EXIT;
   15060              : 
   15061              :    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
   15062            0 :    if (routine_id == RPC_ID_SHUTDOWN)
   15063            0 :       return RPC_SHUTDOWN;
   15064              : 
   15065            0 :    return RPC_SUCCESS;
   15066              : }
   15067              : 
   15068              : /********************************************************************/
   15069            0 : int rpc_test_rpc()
   15070              : /********************************************************************\
   15071              :   Routine: rpc_test_rpc
   15072              : 
   15073              :   Purpose: Test RPC parameters encoding and decoding
   15074              : 
   15075              :   Input:
   15076              :     none
   15077              : 
   15078              :   Output:
   15079              :     none
   15080              : 
   15081              :   Function value:
   15082              :     RPC_SUCCESS             Successful completion
   15083              : 
   15084              : \********************************************************************/
   15085              : {
   15086            0 :    int status = RPC_SUCCESS;
   15087              : 
   15088            0 :    printf("rpc_test_rpc!\n");
   15089              : 
   15090            0 :    int int_out = 0;
   15091            0 :    int int_inout = 456;
   15092              :    
   15093              :    char string_out[32];
   15094              :    char string2_out[48];
   15095              : 
   15096              :    char string_inout[25];
   15097            0 :    strcpy(string_inout, "string_inout");
   15098              : 
   15099              :    KEY struct_in;
   15100              : 
   15101            0 :    struct_in.type = 111;
   15102            0 :    struct_in.num_values = 222;
   15103            0 :    strcpy(struct_in.name, "name");
   15104            0 :    struct_in.last_written = 333;
   15105              :    
   15106              :    KEY struct_out;
   15107              :    KEY struct_inout;
   15108              : 
   15109            0 :    struct_inout.type = 111111;
   15110            0 :    struct_inout.num_values = 222222;
   15111            0 :    strcpy(struct_inout.name, "name_name");
   15112            0 :    struct_inout.last_written = 333333;
   15113              : 
   15114              :    uint32_t dwordarray_inout[10];
   15115            0 :    size_t dwordarray_inout_size = sizeof(dwordarray_inout);
   15116              : 
   15117            0 :    for (int i=0; i<10; i++) {
   15118            0 :       dwordarray_inout[i] = i*10;
   15119              :    }
   15120              : 
   15121              :    char array_in[10];
   15122              : 
   15123            0 :    for (size_t i=0; i<sizeof(array_in); i++) {
   15124            0 :       array_in[i] = 'a' + i;
   15125              :    }
   15126              :    
   15127              :    char array_out[16];
   15128            0 :    size_t array_out_size = sizeof(array_out);
   15129              : 
   15130            0 :    status = rpc_call(RPC_TEST2,
   15131              :                      123,
   15132              :                      &int_out,
   15133              :                      &int_inout,
   15134              :                      "test string",
   15135              :                      string_out, sizeof(string_out),
   15136              :                      string2_out, sizeof(string2_out),
   15137              :                      string_inout, sizeof(string_inout),
   15138              :                      &struct_in,
   15139              :                      &struct_out,
   15140              :                      &struct_inout,
   15141              :                      dwordarray_inout, &dwordarray_inout_size,
   15142              :                      array_in, sizeof(array_in),
   15143              :                      array_out, &array_out_size
   15144              :                      );
   15145              : 
   15146            0 :    printf("rpc_call(RPC_TEST2) status %d\n", status);
   15147              : 
   15148            0 :    if (int_out != 789) {
   15149            0 :       printf("int_out mismatch!\n");
   15150            0 :       status = 0;
   15151              :    }
   15152              : 
   15153            0 :    if (int_inout != 456*2) {
   15154            0 :       printf("int_inout mismatch!\n");
   15155            0 :       status = 0;
   15156              :    }
   15157              : 
   15158            0 :    if (strcmp(string_out, "string_out") != 0) {
   15159            0 :       printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
   15160            0 :       status = 0;
   15161              :    }
   15162              : 
   15163            0 :    if (strcmp(string2_out, "second string_out") != 0) {
   15164            0 :       printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
   15165            0 :       status = 0;
   15166              :    }
   15167              : 
   15168            0 :    if (strcmp(string_inout, "return string_inout") != 0) {
   15169            0 :       printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
   15170            0 :       status = 0;
   15171              :    }
   15172              : 
   15173              :    KEY* pkey;
   15174              : 
   15175            0 :    pkey = &struct_in;
   15176              : 
   15177              :    //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
   15178              : 
   15179            0 :    pkey = &struct_out;
   15180              : 
   15181            0 :    if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
   15182            0 :       printf("struct_out mismatch: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
   15183            0 :       status = 0;
   15184              :    }
   15185              : 
   15186            0 :    pkey = &struct_inout;
   15187              : 
   15188            0 :    if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
   15189            0 :       printf("struct_inout mismatch: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
   15190            0 :       status = 0;
   15191              :    }
   15192              : 
   15193              : #if 0
   15194              :    if (dwordarray_inout_size != 4*5) {
   15195              :       printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
   15196              :       status = 0;
   15197              :    }
   15198              : 
   15199              :    for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
   15200              :       printf("dwordarray_inout[%d] is %d\n", (int)i, dwordarray_inout[i]);
   15201              :    }
   15202              : #endif
   15203              : 
   15204              : #if 0
   15205              :    {RPC_TEST_CXX, "test_cxx",
   15206              :     {{TID_INT32, RPC_IN},
   15207              :      {TID_INT32, RPC_IN | RPC_OUT | RPC_VARARRAY | RPC_CXX},
   15208              :      {TID_STRING, RPC_IN},
   15209              :      {TID_STRING, RPC_IN | RPC_CXX},
   15210              :      {TID_STRING, RPC_OUT | RPC_CXX},
   15211              :      {TID_STRING, RPC_IN | RPC_OUT | RPC_CXX},
   15212              :      {TID_STRUCT, RPC_IN | RPC_CXX, sizeof(KEY)},
   15213              :      {TID_STRUCT, RPC_OUT | RPC_CXX, sizeof(KEY)},
   15214              :      {TID_STRUCT, RPC_IN | RPC_OUT | RPC_CXX, sizeof(KEY)},
   15215              :      {TID_ARRAY, RPC_IN | RPC_VARARRAY | RPC_CXX},
   15216              :      {TID_ARRAY, RPC_OUT | RPC_VARARRAY | RPC_CXX},
   15217              :      {TID_ARRAY, RPC_IN | RPC_OUT | RPC_VARARRAY | RPC_CXX},
   15218              :      {0}}},
   15219              : #endif
   15220              : 
   15221              : #if 0
   15222              :    status = rpc_call(RPC_TEST_CXX, ...);
   15223              : #endif
   15224              : 
   15225            0 :    return status;
   15226              : }
   15227              : 
   15228              : static std::atomic_bool gAllowedHostsEnabled(false);
   15229              : static std::vector<std::string> gAllowedHosts;
   15230              : static std::mutex gAllowedHostsMutex;
   15231              : 
   15232              : /********************************************************************/
   15233            2 : INT rpc_clear_allowed_hosts()
   15234              : /********************************************************************\
   15235              :   Routine: rpc_clear_allowed_hosts
   15236              : 
   15237              :   Purpose: Clear list of allowed hosts and permit connections from anybody
   15238              : 
   15239              :   Input:
   15240              :     none
   15241              : 
   15242              :   Output:
   15243              :     none
   15244              : 
   15245              :   Function value:
   15246              :     RPC_SUCCESS             Successful completion
   15247              : 
   15248              : \********************************************************************/
   15249              : {
   15250            2 :    gAllowedHostsMutex.lock();
   15251            2 :    gAllowedHosts.clear();
   15252            2 :    gAllowedHostsEnabled = false;
   15253            2 :    gAllowedHostsMutex.unlock();
   15254            2 :    return RPC_SUCCESS;
   15255              : }
   15256              : 
   15257              : /********************************************************************/
   15258            2 : INT rpc_add_allowed_host(const char *hostname)
   15259              : /********************************************************************\
   15260              :   Routine: rpc_add_allowed_host
   15261              : 
   15262              :   Purpose: Permit connections from listed hosts only
   15263              : 
   15264              :   Input:
   15265              :     none
   15266              : 
   15267              :   Output:
   15268              :     none
   15269              : 
   15270              :   Function value:
   15271              :     RPC_SUCCESS             Successful completion
   15272              :     RPC_NO_MEMORY           Too many allowed hosts
   15273              : 
   15274              : \********************************************************************/
   15275              : {
   15276              :    //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname); 
   15277              : 
   15278            2 :    gAllowedHostsMutex.lock();
   15279            2 :    gAllowedHosts.push_back(hostname);
   15280            2 :    gAllowedHostsEnabled = true;
   15281            2 :    gAllowedHostsMutex.unlock();
   15282              : 
   15283            2 :    return RPC_SUCCESS;
   15284              : }
   15285              : 
   15286              : /********************************************************************/
   15287            0 : INT rpc_check_allowed_host(const char *hostname)
   15288              : /********************************************************************\
   15289              :   Routine: rpc_check_allowed_host
   15290              : 
   15291              :   Purpose: Check if hostname is permitted to connect
   15292              : 
   15293              :   Function value:
   15294              :     RPC_SUCCESS             hostname is permitted to connect
   15295              :     RPC_NOT_REGISTERED      hostname not permitted to connect
   15296              : 
   15297              : \********************************************************************/
   15298              : {
   15299              :    //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
   15300              : 
   15301            0 :    if (!gAllowedHostsEnabled)
   15302            0 :       return RPC_SUCCESS;
   15303              : 
   15304            0 :    if (strcmp(hostname, "localhost") == 0)
   15305            0 :       return RPC_SUCCESS;
   15306              :    
   15307            0 :    if (strcmp(hostname, "localhost.localdomain") == 0)
   15308            0 :       return RPC_SUCCESS;
   15309              : 
   15310            0 :    if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
   15311            0 :       return RPC_SUCCESS;
   15312              : 
   15313            0 :    if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
   15314            0 :       return RPC_SUCCESS;
   15315              : 
   15316            0 :    int status = RPC_NOT_REGISTERED;
   15317              : 
   15318            0 :    gAllowedHostsMutex.lock();
   15319              : 
   15320            0 :    for (const auto& h: gAllowedHosts) {
   15321            0 :       if (h == hostname) {
   15322            0 :          status = RPC_SUCCESS;
   15323            0 :          break;
   15324              :       }
   15325              :    }
   15326              : 
   15327            0 :    gAllowedHostsMutex.unlock();
   15328              : 
   15329              :    //if (status != RPC_SUCCESS)
   15330              :    //   printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
   15331              : 
   15332            0 :    return status;
   15333              : }
   15334              : 
   15335              : /*------------------------------------------------------------------*/
   15336            0 : static INT rpc_socket_check_allowed_host(int sock)
   15337              : {
   15338            0 :    std::string hostname;
   15339              : 
   15340            0 :    int status = ss_socket_get_peer_name(sock, &hostname, NULL);
   15341              : 
   15342            0 :    if (status != SS_SUCCESS)
   15343            0 :       return status;
   15344              :    
   15345            0 :    status = rpc_check_allowed_host(hostname.c_str());
   15346              :    
   15347            0 :    if (status == RPC_SUCCESS)
   15348            0 :       return RPC_SUCCESS;
   15349              :    
   15350              :    static std::atomic_int max_report(10);
   15351            0 :    if (max_report > 0) {
   15352            0 :       max_report--;
   15353            0 :       if (max_report == 0) {
   15354            0 :          cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
   15355              :       } else {
   15356            0 :          cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\'. Add this host to \"/Experiment/Security/RPC hosts/Allowed hosts\"", hostname.c_str());
   15357              :       }
   15358              :    }
   15359              : 
   15360            0 :    return RPC_NET_ERROR;
   15361            0 : }
   15362              : 
   15363              : /********************************************************************/
   15364            0 : INT rpc_server_accept(int lsock)
   15365              : /********************************************************************\
   15366              : 
   15367              :   Routine: rpc_server_accept
   15368              : 
   15369              :   Purpose: Accept new incoming connections
   15370              : 
   15371              :   Input:
   15372              :     INT    lscok            Listen socket
   15373              : 
   15374              :   Output:
   15375              :     none
   15376              : 
   15377              :   Function value:
   15378              :     RPC_SUCCESS             Successful completion
   15379              :     RPC_NET_ERROR           Error in socket call
   15380              :     RPC_CONNCLOSED          Connection was closed
   15381              :     RPC_SHUTDOWN            Listener shutdown
   15382              :     RPC_EXCEED_BUFFER       Not enough memory for network buffer
   15383              : 
   15384              : \********************************************************************/
   15385              : {
   15386              :    INT i;
   15387              :    INT sock;
   15388              :    char version[NAME_LENGTH], v1[32];
   15389              :    char experiment[NAME_LENGTH];
   15390              :    INT port1, port2, port3;
   15391              :    char *ptr;
   15392              :    char net_buffer[256];
   15393              :    struct linger ling;
   15394              : 
   15395            0 :    static struct callback_addr callback;
   15396              : 
   15397            0 :    if (lsock > 0) {
   15398            0 :       sock = accept(lsock, NULL, NULL);
   15399              : 
   15400            0 :       if (sock == -1)
   15401            0 :          return RPC_NET_ERROR;
   15402              :    } else {
   15403              :       /* lsock is stdin -> already connected from inetd */
   15404              : 
   15405            0 :       sock = lsock;
   15406              :    }
   15407              : 
   15408              :    /* check access control list */
   15409            0 :    if (gAllowedHostsEnabled) {
   15410            0 :       int status = rpc_socket_check_allowed_host(sock);
   15411              : 
   15412            0 :       if (status != RPC_SUCCESS) {
   15413            0 :          ss_socket_close(&sock);
   15414            0 :          return RPC_NET_ERROR;
   15415              :       }
   15416              :    }
   15417              : 
   15418              :    /* receive string with timeout */
   15419            0 :    i = recv_string(sock, net_buffer, 256, 10000);
   15420            0 :    rpc_debug_printf("Received command: %s", net_buffer);
   15421              : 
   15422            0 :    if (i > 0) {
   15423            0 :       char command = (char) toupper(net_buffer[0]);
   15424              : 
   15425              :       //printf("rpc_server_accept: command [%c]\n", command);
   15426              : 
   15427            0 :       switch (command) {
   15428            0 :          case 'S': {
   15429              : 
   15430              :             /*----------- shutdown listener ----------------------*/
   15431            0 :             ss_socket_close(&sock);
   15432            0 :             return RPC_SHUTDOWN;
   15433              :          }
   15434            0 :          case 'I': {
   15435              : 
   15436              :             /*----------- return available experiments -----------*/
   15437              : #ifdef LOCAL_ROUTINES
   15438            0 :             exptab_struct exptab;
   15439            0 :             cm_read_exptab(&exptab); // thread safe!
   15440            0 :             for (unsigned i=0; i<exptab.exptab.size(); i++) {
   15441            0 :                rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
   15442            0 :                const char* str = exptab.exptab[i].name.c_str();
   15443            0 :                send(sock, str, strlen(str) + 1, 0);
   15444              :             }
   15445            0 :             send(sock, "", 1, 0);
   15446              : #endif
   15447            0 :             ss_socket_close(&sock);
   15448            0 :             break;
   15449            0 :          }
   15450            0 :          case 'C': {
   15451              : 
   15452              :             /*----------- connect to experiment -----------*/
   15453              : 
   15454              :             /* get callback information */
   15455            0 :             callback.experiment[0] = 0;
   15456            0 :             port1 = port2 = version[0] = 0;
   15457              : 
   15458              :             //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
   15459              : 
   15460              :             /* parse string in format "C port1 port2 port3 version expt" */
   15461              :             /* example: C 51046 45838 56832 2.0.0 alpha */
   15462              : 
   15463            0 :             port1 = strtoul(net_buffer + 2, &ptr, 0);
   15464            0 :             port2 = strtoul(ptr, &ptr, 0);
   15465            0 :             port3 = strtoul(ptr, &ptr, 0);
   15466              : 
   15467            0 :             while (*ptr == ' ')
   15468            0 :                ptr++;
   15469              : 
   15470            0 :             i = 0;
   15471            0 :             for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
   15472            0 :                version[i++] = *ptr++;
   15473              : 
   15474              :             // ensure that we do not overwrite buffer "version"
   15475            0 :             assert(i < (int) sizeof(version));
   15476            0 :             version[i] = 0;
   15477              : 
   15478              :             // skip wjatever is left from the "version" string
   15479            0 :             for (; *ptr != 0 && *ptr != ' ';)
   15480            0 :                ptr++;
   15481              : 
   15482            0 :             while (*ptr == ' ')
   15483            0 :                ptr++;
   15484              : 
   15485            0 :             i = 0;
   15486            0 :             for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
   15487            0 :                experiment[i++] = *ptr++;
   15488              : 
   15489              :             // ensure that we do not overwrite buffer "experiment"
   15490            0 :             assert(i < (int) sizeof(experiment));
   15491            0 :             experiment[i] = 0;
   15492              : 
   15493            0 :             callback.experiment = experiment;
   15494              : 
   15495              :             /* print warning if version patch level doesn't agree */
   15496            0 :             mstrlcpy(v1, version, sizeof(v1));
   15497            0 :             if (strchr(v1, '.'))
   15498            0 :                if (strchr(strchr(v1, '.') + 1, '.'))
   15499            0 :                   *strchr(strchr(v1, '.') + 1, '.') = 0;
   15500              : 
   15501              :             char str[100];
   15502            0 :             mstrlcpy(str, cm_get_version(), sizeof(str));
   15503            0 :             if (strchr(str, '.'))
   15504            0 :                if (strchr(strchr(str, '.') + 1, '.'))
   15505            0 :                   *strchr(strchr(str, '.') + 1, '.') = 0;
   15506              : 
   15507            0 :             if (strcmp(v1, str) != 0) {
   15508            0 :                cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
   15509            0 :                cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
   15510              :             }
   15511              : 
   15512            0 :             callback.host_port1 = (short) port1;
   15513            0 :             callback.host_port2 = (short) port2;
   15514            0 :             callback.host_port3 = (short) port3;
   15515            0 :             callback.debug = _debug_mode;
   15516              : 
   15517            0 :             int status = ss_socket_get_peer_name(sock, &callback.host_name, NULL);
   15518              : 
   15519            0 :             if (status != SS_SUCCESS) {
   15520            0 :                ss_socket_close(&sock);
   15521            0 :                break;
   15522              :             }
   15523              : 
   15524              : #ifdef LOCAL_ROUTINES
   15525              :             /* update experiment definition */
   15526            0 :             exptab_struct exptab;
   15527            0 :             cm_read_exptab(&exptab); // thread safe!
   15528              : 
   15529            0 :             unsigned idx = 0;
   15530            0 :             bool found = false;
   15531              :             /* lookup experiment */
   15532            0 :             if (equal_ustring(callback.experiment.c_str(), "Default")) {
   15533            0 :                found = true;
   15534            0 :                idx = 0;
   15535              :             } else {
   15536            0 :                for (idx = 0; idx < exptab.exptab.size(); idx++) {
   15537            0 :                   if (exptab.exptab[idx].name == callback.experiment) {
   15538            0 :                      if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
   15539            0 :                         found = true;
   15540            0 :                         break;
   15541              :                      }
   15542              :                   }
   15543              :                }
   15544              :             }
   15545              : 
   15546            0 :             if (!found) {
   15547            0 :                cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
   15548              : 
   15549            0 :                send(sock, "2", 2, 0);   /* 2 means exp. not found */
   15550            0 :                ss_socket_close(&sock);
   15551            0 :                break;
   15552              :             }
   15553              : 
   15554            0 :             callback.directory = exptab.exptab[idx].directory;
   15555            0 :             callback.user = exptab.exptab[idx].user;
   15556              : 
   15557              :             /* create a new process */
   15558              :             char host_port1_str[30], host_port2_str[30], host_port3_str[30];
   15559              :             char debug_str[30];
   15560              : 
   15561            0 :             sprintf(host_port1_str, "%d", callback.host_port1);
   15562            0 :             sprintf(host_port2_str, "%d", callback.host_port2);
   15563            0 :             sprintf(host_port3_str, "%d", callback.host_port3);
   15564            0 :             sprintf(debug_str, "%d", callback.debug);
   15565              : 
   15566            0 :             const char *mserver_path = rpc_get_mserver_path();
   15567              : 
   15568              :             const char *argv[10];
   15569            0 :             argv[0] = mserver_path;
   15570            0 :             argv[1] = callback.host_name.c_str();
   15571            0 :             argv[2] = host_port1_str;
   15572            0 :             argv[3] = host_port2_str;
   15573            0 :             argv[4] = host_port3_str;
   15574            0 :             argv[5] = debug_str;
   15575            0 :             argv[6] = callback.experiment.c_str();
   15576            0 :             argv[7] = callback.directory.c_str();
   15577            0 :             argv[8] = callback.user.c_str();
   15578            0 :             argv[9] = NULL;
   15579              : 
   15580            0 :             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
   15581              :                              argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
   15582              :                              argv[9]);
   15583              : 
   15584            0 :             status = ss_spawnv(P_NOWAIT, mserver_path, argv);
   15585              : 
   15586            0 :             if (status != SS_SUCCESS) {
   15587            0 :                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
   15588              : 
   15589            0 :                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
   15590            0 :                send(sock, str, strlen(str) + 1, 0);
   15591            0 :                ss_socket_close(&sock);
   15592            0 :                break;
   15593              :             }
   15594              : 
   15595            0 :             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
   15596            0 :             send(sock, str, strlen(str) + 1, 0);
   15597              : #endif // LOCAL_ROUTINES
   15598            0 :             ss_socket_close(&sock);
   15599              : 
   15600            0 :             break;
   15601            0 :          }
   15602            0 :          default: {
   15603            0 :             cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
   15604            0 :             ss_socket_close(&sock);
   15605            0 :             break;
   15606              :          }
   15607              :       }
   15608              :    } else {                     /* if i>0 */
   15609              : 
   15610              :       /* lingering needed for PCTCP */
   15611            0 :       ling.l_onoff = 1;
   15612            0 :       ling.l_linger = 0;
   15613            0 :       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
   15614            0 :       ss_socket_close(&sock);
   15615              :    }
   15616              : 
   15617            0 :    return RPC_SUCCESS;
   15618              : }
   15619              : 
   15620              : /********************************************************************/
   15621            0 : INT rpc_client_accept(int lsock)
   15622              : /********************************************************************\
   15623              : 
   15624              :   Routine: rpc_client_accept
   15625              : 
   15626              :   Purpose: midas program accept new RPC connection (run transitions, etc)
   15627              : 
   15628              :   Input:
   15629              :     INT    lsock            Listen socket
   15630              : 
   15631              :   Output:
   15632              :     none
   15633              : 
   15634              :   Function value:
   15635              :     RPC_SUCCESS             Successful completion
   15636              :     RPC_NET_ERROR           Error in socket call
   15637              :     RPC_CONNCLOSED          Connection was closed
   15638              :     RPC_SHUTDOWN            Listener shutdown
   15639              :     RPC_EXCEED_BUFFER       Not enough memory for network buffer
   15640              : 
   15641              : \********************************************************************/
   15642              : {
   15643              :    INT i, status;
   15644            0 :    INT client_hw_type = 0, hw_type;
   15645            0 :    std::string client_program;
   15646            0 :    std::string host_name;
   15647              :    INT convert_flags;
   15648              :    char net_buffer[256], *p;
   15649              : 
   15650            0 :    int sock = accept(lsock, NULL, NULL);
   15651              : 
   15652            0 :    if (sock == -1)
   15653            0 :       return RPC_NET_ERROR;
   15654              : 
   15655              :    /* check access control list */
   15656            0 :    if (gAllowedHostsEnabled) {
   15657            0 :       int status = rpc_socket_check_allowed_host(sock);
   15658              : 
   15659            0 :       if (status != RPC_SUCCESS) {
   15660            0 :          ss_socket_close(&sock);
   15661            0 :          return RPC_NET_ERROR;
   15662              :       }
   15663              :    }
   15664              : 
   15665            0 :    host_name = "(unknown)";
   15666            0 :    client_program = "(unknown)";
   15667              : 
   15668              :    /* receive string with timeout */
   15669            0 :    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
   15670            0 :    if (i <= 0) {
   15671            0 :       ss_socket_close(&sock);
   15672            0 :       return RPC_NET_ERROR;
   15673              :    }
   15674              : 
   15675              :    /* get remote computer info */
   15676            0 :    p = strtok(net_buffer, " ");
   15677            0 :    if (p != NULL) {
   15678            0 :       client_hw_type = atoi(p);
   15679            0 :       p = strtok(NULL, " ");
   15680              :    }
   15681            0 :    if (p != NULL) {
   15682              :       //version = atoi(p);
   15683            0 :       p = strtok(NULL, " ");
   15684              :    }
   15685            0 :    if (p != NULL) {
   15686            0 :       client_program = p;
   15687            0 :       p = strtok(NULL, " ");
   15688              :    }
   15689            0 :    if (p != NULL) {
   15690            0 :       host_name = p;
   15691            0 :       p = strtok(NULL, " ");
   15692              :    }
   15693              : 
   15694              :    //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
   15695              : 
   15696            0 :    RPC_SERVER_ACCEPTION* sa = rpc_new_server_acception();
   15697              : 
   15698              :    /* save information in _server_acception structure */
   15699            0 :    sa->recv_sock = sock;
   15700            0 :    sa->send_sock = 0;
   15701            0 :    sa->event_sock = 0;
   15702            0 :    sa->remote_hw_type = client_hw_type;
   15703            0 :    sa->host_name = host_name;
   15704            0 :    sa->prog_name = client_program;
   15705            0 :    sa->last_activity = ss_millitime();
   15706            0 :    sa->watchdog_timeout = 0;
   15707            0 :    sa->is_mserver = FALSE;
   15708              : 
   15709              :    /* send my own computer id */
   15710            0 :    hw_type = rpc_get_hw_type();
   15711            0 :    std::string str = msprintf("%d %s", hw_type, cm_get_version());
   15712            0 :    status = send(sock, str.c_str(), str.length() + 1, 0);
   15713            0 :    if (status != (INT) str.length() + 1)
   15714            0 :       return RPC_NET_ERROR;
   15715              : 
   15716            0 :    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
   15717            0 :    sa->convert_flags = convert_flags;
   15718              : 
   15719            0 :    ss_suspend_set_server_acceptions(&_server_acceptions);
   15720              : 
   15721            0 :    return RPC_SUCCESS;
   15722            0 : }
   15723              : 
   15724              : /********************************************************************/
   15725            0 : INT rpc_server_callback(struct callback_addr *pcallback)
   15726              : /********************************************************************\
   15727              : 
   15728              :   Routine: rpc_server_callback
   15729              : 
   15730              :   Purpose: Callback a remote client. Setup _server_acception entry
   15731              :            with optional conversion flags and establish two-way
   15732              :            TCP connection.
   15733              : 
   15734              :   Input:
   15735              :     callback_addr pcallback Pointer to a callback structure
   15736              : 
   15737              :   Output:
   15738              :     none
   15739              : 
   15740              :   Function value:
   15741              :     RPC_SUCCESS             Successful completion
   15742              : 
   15743              : \********************************************************************/
   15744              : {
   15745              :    INT status;
   15746              :    int recv_sock, send_sock, event_sock;
   15747              :    char str[100];
   15748            0 :    std::string client_program;
   15749              :    INT client_hw_type, hw_type;
   15750              :    INT convert_flags;
   15751              :    char net_buffer[256];
   15752              :    char *p;
   15753              :    int flag;
   15754              : 
   15755              :    /* copy callback information */
   15756            0 :    struct callback_addr callback = *pcallback;
   15757              :    //idx = callback.index;
   15758              : 
   15759            0 :    std::string errmsg;
   15760              : 
   15761              :    /* create new sockets for TCP */
   15762            0 :    status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port1, &recv_sock, &errmsg);
   15763              : 
   15764            0 :    if (status != SS_SUCCESS) {
   15765            0 :       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket, host \"%s\", port %d: %s", callback.host_name.c_str(), callback.host_port1, errmsg.c_str());
   15766            0 :       ss_socket_close(&recv_sock);
   15767              :       //ss_socket_close(&send_sock);
   15768              :       //ss_socket_close(&event_sock);
   15769            0 :       return RPC_NET_ERROR;
   15770              :    }
   15771              : 
   15772            0 :    status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port2, &send_sock, &errmsg);
   15773              : 
   15774            0 :    if (status != SS_SUCCESS) {
   15775            0 :       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket, host \"%s\", port %d: %s", callback.host_name.c_str(), callback.host_port2, errmsg.c_str());
   15776            0 :       ss_socket_close(&recv_sock);
   15777            0 :       ss_socket_close(&send_sock);
   15778              :       //ss_socket_close(&event_sock);
   15779            0 :       return RPC_NET_ERROR;
   15780              :    }
   15781              : 
   15782            0 :    status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port3, &event_sock, &errmsg);
   15783              : 
   15784            0 :    if (status != SS_SUCCESS) {
   15785            0 :       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket, host \"%s\", port %d: %s", callback.host_name.c_str(), callback.host_port2, errmsg.c_str());
   15786            0 :       ss_socket_close(&recv_sock);
   15787            0 :       ss_socket_close(&send_sock);
   15788            0 :       ss_socket_close(&event_sock);
   15789            0 :       return RPC_NET_ERROR;
   15790              :    }
   15791              : #ifndef OS_ULTRIX               /* crashes ULTRIX... */
   15792              :    /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
   15793            0 :    flag = 2 * 1024 * 1024;
   15794            0 :    status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
   15795            0 :    if (status != 0)
   15796            0 :       cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
   15797            0 :              strerror(errno));
   15798              : #endif
   15799              : 
   15800            0 :    if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
   15801            0 :       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
   15802            0 :       ss_socket_close(&recv_sock);
   15803            0 :       ss_socket_close(&send_sock);
   15804            0 :       ss_socket_close(&event_sock);
   15805            0 :       return RPC_NET_ERROR;
   15806              :    }
   15807              :    //printf("rpc_server_callback: \'%s\'\n", net_buffer);
   15808              : 
   15809              :    /* get remote computer info */
   15810            0 :    client_hw_type = strtoul(net_buffer, &p, 0);
   15811              : 
   15812            0 :    while (*p == ' ')
   15813            0 :       p++;
   15814              : 
   15815            0 :    client_program = p;
   15816              : 
   15817              :    //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
   15818              : 
   15819            0 :    std::string host_name;
   15820              : 
   15821            0 :    status = ss_socket_get_peer_name(recv_sock, &host_name, NULL);
   15822              : 
   15823            0 :    if (status != SS_SUCCESS)
   15824            0 :       host_name = "unknown";
   15825              : 
   15826              :    //printf("rpc_server_callback: mserver acception\n");
   15827              : 
   15828            0 :    RPC_SERVER_ACCEPTION* sa = rpc_new_server_acception();
   15829              : 
   15830              :    /* save information in _server_acception structure */
   15831            0 :    sa->recv_sock = recv_sock;
   15832            0 :    sa->send_sock = send_sock;
   15833            0 :    sa->event_sock = event_sock;
   15834            0 :    sa->remote_hw_type = client_hw_type;
   15835            0 :    sa->host_name = host_name;
   15836            0 :    sa->prog_name = client_program;
   15837            0 :    sa->last_activity = ss_millitime();
   15838            0 :    sa->watchdog_timeout = 0;
   15839            0 :    sa->is_mserver = TRUE;
   15840              : 
   15841            0 :    assert(_mserver_acception == NULL);
   15842              : 
   15843            0 :    _mserver_acception = sa;
   15844              : 
   15845              :    //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
   15846              : 
   15847              :    /* send my own computer id */
   15848            0 :    hw_type = rpc_get_hw_type();
   15849            0 :    sprintf(str, "%d", hw_type);
   15850            0 :    send(recv_sock, str, strlen(str) + 1, 0);
   15851              : 
   15852            0 :    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
   15853            0 :    sa->convert_flags = convert_flags;
   15854              : 
   15855            0 :    ss_suspend_set_server_acceptions(&_server_acceptions);
   15856              : 
   15857            0 :    if (rpc_is_mserver()) {
   15858            0 :       rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
   15859              :    }
   15860              : 
   15861            0 :    return RPC_SUCCESS;
   15862            0 : }
   15863              : 
   15864              : 
   15865              : /********************************************************************/
   15866            0 : INT rpc_server_loop(void)
   15867              : /********************************************************************\
   15868              : 
   15869              :   Routine: rpc_server_loop
   15870              : 
   15871              :   Purpose: mserver main event loop
   15872              : 
   15873              : \********************************************************************/
   15874              : {
   15875              :    while (1) {
   15876            0 :       int status = ss_suspend(1000, 0);
   15877              : 
   15878            0 :       if (status == SS_ABORT || status == SS_EXIT)
   15879              :          break;
   15880              : 
   15881            0 :       if (rpc_check_channels() == RPC_NET_ERROR)
   15882            0 :          break;
   15883              : 
   15884              :       /* check alarms, etc */
   15885            0 :       status = cm_periodic_tasks();
   15886              : 
   15887            0 :       cm_msg_flush_buffer();
   15888            0 :    }
   15889              : 
   15890            0 :    return RPC_SUCCESS;
   15891              : }
   15892              : 
   15893              : /********************************************************************/
   15894            0 : INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION* sa)
   15895              : /********************************************************************\
   15896              : 
   15897              :   Routine: rpc_server_receive_rpc
   15898              : 
   15899              :   Purpose: Receive rpc commands and execute them. Close the connection
   15900              :            if client has broken TCP pipe.
   15901              : 
   15902              :   Function value:
   15903              :     RPC_SUCCESS             Successful completion
   15904              :     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
   15905              :     SS_EXIT                 Server connection was closed
   15906              :     SS_ABORT                Server connection was broken
   15907              : 
   15908              : \********************************************************************/
   15909              : {
   15910            0 :    int status = 0;
   15911            0 :    int remaining = 0;
   15912              : 
   15913            0 :    char *buf = NULL;
   15914            0 :    int bufsize = 0;
   15915              :    
   15916              :    do {
   15917            0 :       int n_received = recv_net_command_realloc(idx, &buf, &bufsize, &remaining);
   15918              :       
   15919            0 :       if (n_received <= 0) {
   15920            0 :          status = SS_ABORT;
   15921            0 :          cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d, abort", n_received);
   15922            0 :          goto error;
   15923              :       }
   15924              :       
   15925            0 :       status = rpc_execute(sa->recv_sock, buf, sa->convert_flags);
   15926              :       
   15927            0 :       if (status == SS_ABORT) {
   15928            0 :          cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
   15929            0 :          goto error;
   15930              :       }
   15931              :       
   15932            0 :          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
   15933            0 :             if (rpc_is_mserver())
   15934            0 :                rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
   15935            0 :             goto exit;
   15936              :          }
   15937              :          
   15938            0 :    } while (remaining);
   15939              :    
   15940            0 :    if (buf) {
   15941            0 :       free(buf);
   15942            0 :       buf = NULL;
   15943            0 :       bufsize = 0;
   15944              :    }
   15945              : 
   15946            0 :    return RPC_SUCCESS;
   15947              : 
   15948            0 :    error:
   15949              : 
   15950              :    {
   15951              :       char str[80];
   15952            0 :       mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
   15953            0 :       if (strchr(str, '.'))
   15954            0 :          *strchr(str, '.') = 0;
   15955            0 :       cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
   15956              :    }
   15957              : 
   15958            0 :    exit:
   15959              : 
   15960            0 :    cm_msg_flush_buffer();
   15961              : 
   15962            0 :    if (buf) {
   15963            0 :       free(buf);
   15964            0 :       buf = NULL;
   15965            0 :       bufsize = 0;
   15966              :    }
   15967              : 
   15968              :    /* disconnect from experiment as MIDAS server */
   15969            0 :    if (rpc_is_mserver()) {
   15970              :       HNDLE hDB, hKey;
   15971              : 
   15972            0 :       cm_get_experiment_database(&hDB, &hKey);
   15973              : 
   15974              :       /* only disconnect from experiment if previously connected.
   15975              :          Necessary for pure RPC servers (RPC_SRVR) */
   15976            0 :       if (hDB) {
   15977            0 :          bm_close_all_buffers();
   15978            0 :          cm_delete_client_info(hDB, 0);
   15979            0 :          db_close_all_databases();
   15980              : 
   15981            0 :          rpc_deregister_functions();
   15982              : 
   15983            0 :          cm_set_experiment_database(0, 0);
   15984              :       }
   15985              :    }
   15986              : 
   15987            0 :    bool is_mserver = sa->is_mserver;
   15988              : 
   15989            0 :    sa->close();
   15990              : 
   15991              :    /* signal caller a shutdonw */
   15992            0 :    if (status == RPC_SHUTDOWN)
   15993            0 :       return status;
   15994              : 
   15995              :    /* only the mserver should stop on server connection closure */
   15996            0 :    if (!is_mserver) {
   15997            0 :       return SS_SUCCESS;
   15998              :    }
   15999              : 
   16000            0 :    return status;
   16001              : }
   16002              : 
   16003              : /********************************************************************/
   16004            0 : INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION* sa, int timeout_msec)
   16005              : /********************************************************************\
   16006              : 
   16007              :   Routine: rpc_server_receive_event
   16008              : 
   16009              :   Purpose: Receive event and dispatch it
   16010              : 
   16011              :   Function value:
   16012              :     RPC_SUCCESS             Successful completion
   16013              :     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
   16014              :     SS_EXIT                 Server connection was closed
   16015              :     SS_ABORT                Server connection was broken
   16016              : 
   16017              : \********************************************************************/
   16018              : {
   16019            0 :    int status = 0;
   16020              : 
   16021            0 :    DWORD start_time = ss_millitime();
   16022              : 
   16023              :    //
   16024              :    // THIS IS NOT THREAD SAFE!!!
   16025              :    //
   16026              :    // IT IS ONLY USED BY THE MSERVER
   16027              :    // MSERVER IS SINGLE-THREADED!!!
   16028              :    //
   16029              : 
   16030              :    static char *xbuf = NULL;
   16031              :    static int   xbufsize = 0;
   16032              :    static bool  xbufempty = true;
   16033              : 
   16034              :    // short cut
   16035            0 :    if (sa == NULL && xbufempty)
   16036            0 :       return RPC_SUCCESS;
   16037              : 
   16038              :    static bool  recurse = false;
   16039              : 
   16040            0 :    if (recurse) {
   16041            0 :       cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
   16042              :       // do not do anything if we are called recursively
   16043              :       // via recursive ss_suspend() or otherwise. K.O.
   16044            0 :       if (xbufempty)
   16045            0 :          return RPC_SUCCESS;
   16046              :       else
   16047            0 :          return BM_ASYNC_RETURN;
   16048              :    }
   16049              : 
   16050            0 :    recurse = true;
   16051              :    
   16052              :    do {
   16053            0 :       if (xbufempty && sa) {
   16054            0 :          int n_received = recv_event_server_realloc(idx, sa, &xbuf, &xbufsize);
   16055              :       
   16056            0 :          if (n_received < 0) {
   16057            0 :             status = SS_ABORT;
   16058            0 :             cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
   16059            0 :             goto error;
   16060              :          }
   16061              : 
   16062            0 :          if (n_received == 0) {
   16063              :             // no more data in the tcp socket
   16064            0 :             recurse = false;
   16065            0 :             return RPC_SUCCESS;
   16066              :          }
   16067              : 
   16068            0 :          xbufempty = false;
   16069              :       }
   16070              : 
   16071            0 :       if (xbufempty) {
   16072              :          // no event in xbuf buffer
   16073            0 :          recurse = false;
   16074            0 :          return RPC_SUCCESS;
   16075              :       }
   16076              : 
   16077              :       /* send event to buffer */
   16078            0 :       INT *pbh = (INT *) xbuf;
   16079            0 :       EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
   16080              :       
   16081            0 :       status = bm_send_event(*pbh, pevent, 0, timeout_msec);
   16082              : 
   16083              :       //printf("rpc_server_receiv: buffer_handle %d, event_id 0x%04x, serial 0x%08x, data_size %d, status %d\n", *pbh, pevent->event_id, pevent->serial_number, pevent->data_size, status);
   16084              :       
   16085            0 :       if (status == SS_ABORT) {
   16086            0 :          cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
   16087            0 :          goto error;
   16088              :       }
   16089              :       
   16090            0 :       if (status == BM_ASYNC_RETURN) {
   16091              :          //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
   16092            0 :          recurse = false;
   16093            0 :          return status;
   16094              :       }
   16095              :       
   16096            0 :       if (status != BM_SUCCESS) {
   16097            0 :          cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
   16098              :       }
   16099              :       
   16100            0 :       xbufempty = true;
   16101              : 
   16102              :       /* repeat for maximum 0.5 sec */
   16103            0 :    } while (ss_millitime() - start_time < 500);
   16104              :    
   16105            0 :    recurse = false;
   16106            0 :    return RPC_SUCCESS;
   16107              : 
   16108            0 :    error:
   16109              : 
   16110              :    {
   16111              :       char str[80];
   16112            0 :       mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
   16113            0 :       if (strchr(str, '.'))
   16114            0 :          *strchr(str, '.') = 0;
   16115            0 :       cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
   16116              :    }
   16117              : 
   16118              :    //exit:
   16119              : 
   16120            0 :    cm_msg_flush_buffer();
   16121              : 
   16122              :    /* disconnect from experiment as MIDAS server */
   16123            0 :    if (rpc_is_mserver()) {
   16124              :       HNDLE hDB, hKey;
   16125              : 
   16126            0 :       cm_get_experiment_database(&hDB, &hKey);
   16127              : 
   16128              :       /* only disconnect from experiment if previously connected.
   16129              :          Necessary for pure RPC servers (RPC_SRVR) */
   16130            0 :       if (hDB) {
   16131            0 :          bm_close_all_buffers();
   16132            0 :          cm_delete_client_info(hDB, 0);
   16133            0 :          db_close_all_databases();
   16134              : 
   16135            0 :          rpc_deregister_functions();
   16136              : 
   16137            0 :          cm_set_experiment_database(0, 0);
   16138              :       }
   16139              :    }
   16140              : 
   16141            0 :    bool is_mserver = sa->is_mserver;
   16142              : 
   16143            0 :    sa->close();
   16144              : 
   16145              :    /* signal caller a shutdonw */
   16146            0 :    if (status == RPC_SHUTDOWN)
   16147            0 :       return status;
   16148              : 
   16149              :    /* only the mserver should stop on server connection closure */
   16150            0 :    if (!is_mserver) {
   16151            0 :       return SS_SUCCESS;
   16152              :    }
   16153              : 
   16154            0 :    return status;
   16155              : }
   16156              : 
   16157              : 
   16158              : /********************************************************************/
   16159            0 : int rpc_flush_event_socket(int timeout_msec)
   16160              : /********************************************************************\
   16161              : 
   16162              :   Routine: rpc_flush_event_socket
   16163              : 
   16164              :   Purpose: Receive and en-buffer events from the mserver event socket
   16165              : 
   16166              :   Function value:
   16167              :     BM_SUCCESS              Event socket is empty, all data was read an en-buffered
   16168              :     BM_ASYNC_RETURN         Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
   16169              :     SS_EXIT                 Server connection was closed
   16170              :     SS_ABORT                Server connection was broken
   16171              : 
   16172              : \********************************************************************/
   16173              : {
   16174            0 :    bool has_data = ss_event_socket_has_data();
   16175              :    
   16176              :    //printf("ss_event_socket_has_data() returned %d\n", has_data);
   16177              : 
   16178            0 :    if (has_data) {
   16179            0 :       if (timeout_msec == BM_NO_WAIT) {
   16180            0 :          return BM_ASYNC_RETURN;
   16181            0 :       } else if (timeout_msec == BM_WAIT) {
   16182            0 :          return BM_ASYNC_RETURN;
   16183              :       } else {
   16184            0 :          int status = ss_suspend(timeout_msec, MSG_BM);
   16185            0 :          if (status == SS_ABORT || status == SS_EXIT)
   16186            0 :             return status;
   16187            0 :          return BM_ASYNC_RETURN;
   16188              :       }
   16189              :    }
   16190              : 
   16191            0 :    int status = rpc_server_receive_event(0, NULL, timeout_msec);
   16192              :    
   16193              :    //printf("rpc_server_receive_event() status %d\n", status);
   16194              : 
   16195            0 :    if (status == BM_ASYNC_RETURN) {
   16196            0 :       return BM_ASYNC_RETURN;
   16197              :    }
   16198              : 
   16199            0 :    if (status == SS_ABORT || status == SS_EXIT)
   16200            0 :       return status;
   16201              :    
   16202            0 :    return BM_SUCCESS;
   16203              : }
   16204              : 
   16205              : /********************************************************************/
   16206            2 : INT rpc_server_shutdown(void)
   16207              : /********************************************************************\
   16208              : 
   16209              :   Routine: rpc_server_shutdown
   16210              : 
   16211              :   Purpose: Shutdown RPC server, abort all connections
   16212              : 
   16213              :   Input:
   16214              :     none
   16215              : 
   16216              :   Output:
   16217              :     none
   16218              : 
   16219              :   Function value:
   16220              :     RPC_SUCCESS             Successful completion
   16221              : 
   16222              : \********************************************************************/
   16223              : {
   16224              :    //printf("rpc_server_shutdown!\n");
   16225              : 
   16226              :    struct linger ling;
   16227              : 
   16228              :    /* close all open connections */
   16229            2 :    for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
   16230            0 :       if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
   16231            0 :          RPC_SERVER_ACCEPTION* sa = _server_acceptions[idx];
   16232              :          /* lingering needed for PCTCP */
   16233            0 :          ling.l_onoff = 1;
   16234            0 :          ling.l_linger = 0;
   16235            0 :          setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
   16236            0 :          ss_socket_close(&sa->recv_sock);
   16237              : 
   16238            0 :          if (sa->send_sock) {
   16239            0 :             setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
   16240            0 :             ss_socket_close(&sa->send_sock);
   16241              :          }
   16242              : 
   16243            0 :          if (sa->event_sock) {
   16244            0 :             setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
   16245            0 :             ss_socket_close(&sa->event_sock);
   16246              :          }
   16247              :       }
   16248              :    }
   16249              : 
   16250              :    /* avoid memory leak */
   16251            2 :    for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
   16252            0 :       RPC_SERVER_ACCEPTION* sa = _server_acceptions[idx];
   16253            0 :       if (sa) {
   16254              :          //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
   16255            0 :          if (sa == _mserver_acception) {
   16256              :             // do not leave behind a stale pointer!
   16257            0 :             _mserver_acception = NULL;
   16258              :          }
   16259            0 :          delete sa;
   16260            0 :          _server_acceptions[idx] = NULL;
   16261              :       }
   16262              :    }
   16263              : 
   16264            2 :    if (_rpc_registered) {
   16265            2 :       ss_socket_close(&_rpc_listen_socket);
   16266            2 :       _rpc_registered = FALSE;
   16267              :    }
   16268              : 
   16269              :    /* free suspend structures */
   16270            2 :    ss_suspend_exit();
   16271              : 
   16272            2 :    return RPC_SUCCESS;
   16273              : }
   16274              : 
   16275              : 
   16276              : /********************************************************************/
   16277            0 : INT rpc_check_channels(void)
   16278              : /********************************************************************\
   16279              : 
   16280              :   Routine: rpc_check_channels
   16281              : 
   16282              :   Purpose: Check open rpc channels by sending watchdog messages
   16283              : 
   16284              :   Input:
   16285              :     none
   16286              : 
   16287              :   Output:
   16288              :     none
   16289              : 
   16290              :   Function value:
   16291              :     RPC_SUCCESS             Channel is still alive
   16292              :     RPC_NET_ERROR           Connection is broken
   16293              : 
   16294              : \********************************************************************/
   16295              : {
   16296              :    INT status;
   16297              :    NET_COMMAND nc;
   16298              :    fd_set readfds;
   16299              :    struct timeval timeout;
   16300              : 
   16301            0 :    for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
   16302            0 :       if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
   16303            0 :          RPC_SERVER_ACCEPTION* sa = _server_acceptions[idx];
   16304            0 :          if (sa == NULL)
   16305            0 :             continue;
   16306              :          
   16307            0 :          if (sa->watchdog_timeout == 0) {
   16308            0 :             continue;
   16309              :          }
   16310              : 
   16311            0 :          DWORD elapsed = ss_millitime() - sa->last_activity;
   16312              :          //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
   16313              : 
   16314            0 :          if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
   16315              :          
   16316              :             //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
   16317              : 
   16318              :             /* send a watchdog message */
   16319            0 :             nc.header.routine_id = MSG_WATCHDOG;
   16320            0 :             nc.header.param_size = 0;
   16321              : 
   16322            0 :             int convert_flags = sa->convert_flags;
   16323            0 :             if (convert_flags) {
   16324            0 :                rpc_convert_single(&nc.header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
   16325            0 :                rpc_convert_single(&nc.header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
   16326              :             }
   16327              : 
   16328              :             /* send the header to the client */
   16329            0 :             int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
   16330              : 
   16331            0 :             if (i < 0) {
   16332            0 :                cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
   16333              :                       sa->prog_name.c_str(),
   16334              :                       sa->host_name.c_str(),
   16335            0 :                       sa->watchdog_timeout / 1000,
   16336              :                       i);
   16337              :                
   16338              :                /* disconnect from experiment */
   16339            0 :                if (rpc_is_mserver()) {
   16340            0 :                   cm_disconnect_experiment();
   16341            0 :                   return RPC_NET_ERROR;
   16342              :                }
   16343              :                
   16344            0 :                sa->close();
   16345            0 :                return RPC_NET_ERROR;
   16346              :             }
   16347              : 
   16348              :             /* make some timeout checking */
   16349            0 :             FD_ZERO(&readfds);
   16350            0 :             FD_SET(sa->send_sock, &readfds);
   16351            0 :             FD_SET(sa->recv_sock, &readfds);
   16352              : 
   16353            0 :             timeout.tv_sec = sa->watchdog_timeout / 1000;
   16354            0 :             timeout.tv_usec = (sa->watchdog_timeout % 1000) * 1000;
   16355              : 
   16356              :             do {
   16357            0 :                status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
   16358              : 
   16359              :                /* if an alarm signal was cought, restart select with reduced timeout */
   16360            0 :                if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
   16361            0 :                   timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
   16362              : 
   16363            0 :             } while (status == -1);        /* dont return if an alarm signal was cought */
   16364              : 
   16365            0 :             if (!FD_ISSET(sa->send_sock, &readfds) &&
   16366            0 :                 !FD_ISSET(sa->recv_sock, &readfds)) {
   16367              : 
   16368            0 :                cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
   16369              :                       sa->prog_name.c_str(),
   16370              :                       sa->host_name.c_str(),
   16371            0 :                       sa->watchdog_timeout / 1000);
   16372              :                
   16373              :                /* disconnect from experiment */
   16374            0 :                if (rpc_is_mserver()) {
   16375            0 :                   cm_disconnect_experiment();
   16376            0 :                   return RPC_NET_ERROR;
   16377              :                }               
   16378              : 
   16379            0 :                sa->close();
   16380            0 :                return RPC_NET_ERROR;
   16381              :             }
   16382              : 
   16383              :             /* receive result on send socket */
   16384            0 :             if (FD_ISSET(sa->send_sock, &readfds)) {
   16385            0 :                i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
   16386            0 :                if (i <= 0) {
   16387            0 :                   cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
   16388              :                          sa->prog_name.c_str(),
   16389              :                          sa->host_name.c_str(),
   16390            0 :                          sa->watchdog_timeout / 1000,
   16391              :                          i);
   16392              :                   
   16393              :                   /* disconnect from experiment */
   16394            0 :                   if (rpc_is_mserver()) {
   16395            0 :                      cm_disconnect_experiment();
   16396            0 :                      return RPC_NET_ERROR;
   16397              :                   }
   16398              :                   
   16399            0 :                   sa->close();
   16400            0 :                   return RPC_NET_ERROR;
   16401              :                }
   16402              :             }
   16403              :          }
   16404              :       }
   16405              :    }
   16406              : 
   16407            0 :    return RPC_SUCCESS;
   16408              : }
   16409              : 
   16410              : /** @} */
   16411              : 
   16412              : /**dox***************************************************************/
   16413              : /** @addtogroup bkfunctionc
   16414              :  *
   16415              :  *  @{  */
   16416              : 
   16417              : /********************************************************************\
   16418              : *                                                                    *
   16419              : *                 Bank functions                                     *
   16420              : *                                                                    *
   16421              : \********************************************************************/
   16422              : 
   16423              : /********************************************************************/
   16424              : /**
   16425              : Initializes an event for Midas banks structure.
   16426              : Before banks can be created in an event, bk_init() has to be called first.
   16427              : @param event pointer to the area of event
   16428              : */
   16429            0 : void bk_init(void *event) {
   16430            0 :    ((BANK_HEADER *) event)->data_size = 0;
   16431            0 :    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
   16432            0 : }
   16433              : 
   16434              : /**dox***************************************************************/
   16435              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   16436              : 
   16437              : /********************************************************************/
   16438            0 : BOOL bk_is32(const void *event)
   16439              : /********************************************************************\
   16440              : 
   16441              :   Routine: bk_is32
   16442              : 
   16443              :   Purpose: Return true if banks inside event are 32-bit banks
   16444              : 
   16445              :   Input:
   16446              :     void   *event           pointer to the event
   16447              : 
   16448              :   Output:
   16449              :     none
   16450              : 
   16451              :   Function value:
   16452              :     none
   16453              : 
   16454              : \********************************************************************/
   16455              : {
   16456            0 :    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
   16457              : }
   16458              : 
   16459              : /********************************************************************/
   16460            0 : BOOL bk_is32a(const void *event)
   16461              : /********************************************************************\
   16462              : 
   16463              :   Routine: bk_is32a
   16464              : 
   16465              :   Purpose: Return true if banks inside event are 32-bit banks
   16466              :            and banks are 64-bit aligned
   16467              : 
   16468              :   Input:
   16469              :     void   *event           pointer to the event
   16470              : 
   16471              :   Output:
   16472              :     none
   16473              : 
   16474              :   Function value:
   16475              :     none
   16476              : 
   16477              : \********************************************************************/
   16478              : {
   16479            0 :    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
   16480              : }
   16481              : 
   16482              : /**dox***************************************************************/
   16483              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   16484              : 
   16485              : /********************************************************************/
   16486              : /**
   16487              : Initializes an event for Midas banks structure for large bank size (> 32KBytes)
   16488              : Before banks can be created in an event, bk_init32() has to be called first.
   16489              : @param event pointer to the area of event
   16490              : @return void
   16491              : */
   16492            0 : void bk_init32(void *event) {
   16493            0 :    ((BANK_HEADER *) event)->data_size = 0;
   16494            0 :    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
   16495            0 : }
   16496              : 
   16497              : /********************************************************************/
   16498              : /**
   16499              : Initializes an event for Midas banks structure for large bank size (> 32KBytes)
   16500              : which are aligned on 64-bit boundaries.
   16501              : Before banks can be created in an event, bk_init32a() has to be called first.
   16502              : @param event pointer to the area of event
   16503              : @return void
   16504              : */
   16505            0 : void bk_init32a(void *event) {
   16506            0 :    ((BANK_HEADER *) event)->data_size = 0;
   16507            0 :    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT | BANK_FORMAT_64BIT_ALIGNED;
   16508            0 : }
   16509              : 
   16510              : /********************************************************************/
   16511              : /**
   16512              : Returns the size of an event containing banks.
   16513              : The total size of an event is the value returned by bk_size() plus the size
   16514              : of the event header (sizeof(EVENT_HEADER)).
   16515              : @param event pointer to the area of event
   16516              : @return number of bytes contained in data area of event
   16517              : */
   16518            0 : INT bk_size(const void *event) {
   16519            0 :    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
   16520              : }
   16521              : 
   16522            0 : static void copy_bk_name(char* dst, const char* src)
   16523              : {
   16524              :    // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
   16525              : 
   16526            0 :    if (src[0] == 0) {
   16527              :       // invalid empty name
   16528            0 :       dst[0] = 0;
   16529            0 :       dst[1] = 0;
   16530            0 :       dst[2] = 0;
   16531            0 :       dst[3] = 0;
   16532            0 :       return;
   16533              :    }
   16534              : 
   16535            0 :    dst[0] = src[0];
   16536              : 
   16537            0 :    if (src[1] == 0) {
   16538            0 :       dst[1] = 0;
   16539            0 :       dst[2] = 0;
   16540            0 :       dst[3] = 0;
   16541            0 :       return;
   16542              :    }
   16543              : 
   16544            0 :    dst[1] = src[1];
   16545              : 
   16546            0 :    if (src[2] == 0) {
   16547            0 :       dst[2] = 0;
   16548            0 :       dst[3] = 0;
   16549            0 :       return;
   16550              :    }
   16551              : 
   16552            0 :    dst[2] = src[2];
   16553              : 
   16554            0 :    if (src[3] == 0) {
   16555            0 :       dst[3] = 0;
   16556            0 :       return;
   16557              :    }
   16558              : 
   16559            0 :    dst[3] = src[3];
   16560              : }
   16561              : 
   16562              : /********************************************************************/
   16563              : /**
   16564              : Create a Midas bank.
   16565              : The data pointer pdata must be used as an address to fill a
   16566              : bank. It is incremented with every value written to the bank and finally points
   16567              : to a location just after the last byte of the bank. It is then passed to
   16568              : the function bk_close() to finish the bank creation.
   16569              : \code
   16570              : INT *pdata;
   16571              : bk_init(pevent);
   16572              : bk_create(pevent, "ADC0", TID_INT32, &pdata);
   16573              : *pdata++ = 123
   16574              : *pdata++ = 456
   16575              : bk_close(pevent, pdata);
   16576              : \endcode
   16577              : @param event pointer to the data area
   16578              : @param name of the bank, must be exactly 4 charaters
   16579              : @param type type of bank, one of the @ref Midas_Data_Types values defined in
   16580              : midas.h
   16581              : @param pdata pointer to the data area of the newly created bank
   16582              : @return void
   16583              : */
   16584            0 : void bk_create(void *event, const char *name, WORD type, void **pdata) {
   16585            0 :    if (bk_is32a((BANK_HEADER *) event)) {
   16586            0 :       if (((PTYPE) event & 0x07) != 0) {
   16587            0 :          cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
   16588            0 :          return;
   16589              :       }
   16590              :       BANK32A *pbk32a;
   16591              : 
   16592            0 :       pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
   16593            0 :       copy_bk_name(pbk32a->name, name);
   16594            0 :       pbk32a->type = type;
   16595            0 :       pbk32a->data_size = 0;
   16596            0 :       *pdata = pbk32a + 1;
   16597            0 :    } else if (bk_is32((BANK_HEADER *) event)) {
   16598              :       BANK32 *pbk32;
   16599              : 
   16600            0 :       pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
   16601            0 :       copy_bk_name(pbk32->name, name);
   16602            0 :       pbk32->type = type;
   16603            0 :       pbk32->data_size = 0;
   16604            0 :       *pdata = pbk32 + 1;
   16605              :    } else {
   16606              :       BANK *pbk;
   16607              : 
   16608            0 :       pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
   16609            0 :       copy_bk_name(pbk->name, name);
   16610            0 :       pbk->type = type;
   16611            0 :       pbk->data_size = 0;
   16612            0 :       *pdata = pbk + 1;
   16613              :    }
   16614              : }
   16615              : 
   16616              : /**dox***************************************************************/
   16617              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   16618              : 
   16619              : /********************************************************************/
   16620              : /**
   16621              : Copy a bank given by name if found from a buffer source to a destination buffer.
   16622              : @param * pevent pointing after the EVENT_HEADER (as in FE)
   16623              : @param * psrce  pointing to EVENT_HEADER in this case (for ebch[i].pfragment)
   16624              : @param bkname  Bank to be found and copied from psrce to pevent
   16625              : @return EB_SUCCESS if bank found, 0 if not found (pdest untouched)
   16626              : */
   16627            0 : INT bk_copy(char *pevent, char *psrce, const char *bkname) {
   16628              : 
   16629              :    INT status;
   16630              :    DWORD bklen, bktype, bksze;
   16631              :    BANK_HEADER *psBkh;
   16632              :    BANK *psbkh;
   16633              :    char *pdest;
   16634              :    void *psdata;
   16635              : 
   16636              :    // source pointing on the BANKxx
   16637            0 :    psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
   16638              :    // Find requested bank
   16639            0 :    status = bk_find(psBkh, bkname, &bklen, &bktype, &psdata);
   16640              :    // Return 0 if not found
   16641            0 :    if (status != SUCCESS) return 0;
   16642              : 
   16643              :    // Check bank type...
   16644              :    // You cannot mix BANK and BANK32 so make sure all the FE use either
   16645              :    // bk_init(pevent) or bk_init32(pevent).
   16646            0 :    if (bk_is32a(psBkh)) {
   16647              : 
   16648              :       // pointer to the source bank header
   16649            0 :       BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
   16650              :       // Data size in the bank
   16651            0 :       bksze = psbkh32a->data_size;
   16652              : 
   16653              :       // Get to the end of the event
   16654            0 :       pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
   16655              :       // Copy from BANK32 to end of Data
   16656            0 :       memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
   16657              :       // Bring pointer to the next free location
   16658            0 :       pdest += ALIGN8(bksze) + sizeof(BANK32A);
   16659              : 
   16660            0 :    } else if (bk_is32(psBkh)) {
   16661              : 
   16662              :       // pointer to the source bank header
   16663            0 :       BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
   16664              :       // Data size in the bank
   16665            0 :       bksze = psbkh32->data_size;
   16666              : 
   16667              :       // Get to the end of the event
   16668            0 :       pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
   16669              :       // Copy from BANK32 to end of Data
   16670            0 :       memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
   16671              :       // Bring pointer to the next free location
   16672            0 :       pdest += ALIGN8(bksze) + sizeof(BANK32);
   16673              : 
   16674              :    } else {
   16675              : 
   16676              :       // pointer to the source bank header
   16677            0 :       psbkh = ((BANK *) psdata - 1);
   16678              :       // Data size in the bank
   16679            0 :       bksze = psbkh->data_size;
   16680              : 
   16681              :       // Get to the end of the event
   16682            0 :       pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
   16683              :       // Copy from BANK to end of Data
   16684            0 :       memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
   16685              :       // Bring pointer to the next free location
   16686            0 :       pdest += ALIGN8(bksze) + sizeof(BANK);
   16687              :    }
   16688              : 
   16689              :    // Close bank (adjust BANK_HEADER size)
   16690            0 :    bk_close(pevent, pdest);
   16691              :    // Adjust EVENT_HEADER size
   16692            0 :    ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
   16693            0 :    return SUCCESS;
   16694              : }
   16695              : 
   16696              : /********************************************************************/
   16697            0 : int bk_delete(void *event, const char *name)
   16698              : /********************************************************************\
   16699              : 
   16700              :   Routine: bk_delete
   16701              : 
   16702              :   Purpose: Delete a MIDAS bank inside an event
   16703              : 
   16704              :   Input:
   16705              :     void   *event           pointer to the event
   16706              :     char   *name            Name of bank (exactly four letters)
   16707              : 
   16708              :   Function value:
   16709              :     CM_SUCCESS              Bank has been deleted
   16710              :     0                       Bank has not been found
   16711              : 
   16712              : \********************************************************************/
   16713              : {
   16714              :    BANK *pbk;
   16715              :    DWORD dname;
   16716              :    int remaining;
   16717              : 
   16718            0 :    if (bk_is32a((BANK_HEADER *) event)) {
   16719              :       /* locate bank */
   16720            0 :       BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
   16721            0 :       copy_bk_name((char *) &dname, name);
   16722              :       do {
   16723            0 :          if (*((DWORD *) pbk32a->name) == dname) {
   16724              :             /* bank found, delete it */
   16725            0 :             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
   16726            0 :                          sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
   16727              : 
   16728              :             /* reduce total event size */
   16729            0 :             ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
   16730              : 
   16731              :             /* copy remaining bytes */
   16732            0 :             if (remaining > 0)
   16733            0 :                memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
   16734            0 :             return CM_SUCCESS;
   16735              :          }
   16736              : 
   16737            0 :          pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
   16738            0 :       } while ((DWORD) ((char *) pbk32a - (char *) event) <
   16739            0 :                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
   16740            0 :    } else if (bk_is32((BANK_HEADER *) event)) {
   16741              :       /* locate bank */
   16742            0 :       BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
   16743            0 :       copy_bk_name((char *) &dname, name);
   16744              :       do {
   16745            0 :          if (*((DWORD *) pbk32->name) == dname) {
   16746              :             /* bank found, delete it */
   16747            0 :             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
   16748            0 :                          sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
   16749              : 
   16750              :             /* reduce total event size */
   16751            0 :             ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
   16752              : 
   16753              :             /* copy remaining bytes */
   16754            0 :             if (remaining > 0)
   16755            0 :                memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
   16756            0 :             return CM_SUCCESS;
   16757              :          }
   16758              : 
   16759            0 :          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
   16760            0 :       } while ((DWORD) ((char *) pbk32 - (char *) event) <
   16761            0 :                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
   16762              :    } else {
   16763              :       /* locate bank */
   16764            0 :       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
   16765            0 :       copy_bk_name((char *) &dname, name);
   16766              :       do {
   16767            0 :          if (*((DWORD *) pbk->name) == dname) {
   16768              :             /* bank found, delete it */
   16769            0 :             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
   16770            0 :                          sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
   16771              : 
   16772              :             /* reduce total event size */
   16773            0 :             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
   16774              : 
   16775              :             /* copy remaining bytes */
   16776            0 :             if (remaining > 0)
   16777            0 :                memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
   16778            0 :             return CM_SUCCESS;
   16779              :          }
   16780              : 
   16781            0 :          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
   16782            0 :       } while ((DWORD) ((char *) pbk - (char *) event) <
   16783            0 :                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
   16784              :    }
   16785              : 
   16786            0 :    return 0;
   16787              : }
   16788              : 
   16789              : /**dox***************************************************************/
   16790              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   16791              : 
   16792              : /********************************************************************/
   16793              : /**
   16794              : Close the Midas bank priviously created by bk_create().
   16795              : The data pointer pdata must be obtained by bk_create() and
   16796              : used as an address to fill a bank. It is incremented with every value written
   16797              : to the bank and finally points to a location just after the last byte of the
   16798              : bank. It is then passed to bk_close() to finish the bank creation
   16799              : @param event pointer to current composed event
   16800              : @param pdata  pointer to the data
   16801              : @return number of bytes contained in bank
   16802              : */
   16803            0 : INT bk_close(void *event, void *pdata) {
   16804            0 :    if (bk_is32a((BANK_HEADER *) event)) {
   16805            0 :       BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
   16806            0 :       pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
   16807            0 :       if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
   16808            0 :          printf("Warning: TID_STRUCT bank %c%c%c%c has zero size\n", pbk32a->name[0], pbk32a->name[1], pbk32a->name[2], pbk32a->name[3]);
   16809            0 :       ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
   16810            0 :       return pbk32a->data_size;
   16811            0 :    } else if (bk_is32((BANK_HEADER *) event)) {
   16812            0 :       BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
   16813            0 :       pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
   16814            0 :       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
   16815            0 :          printf("Warning: TID_STRUCT bank %c%c%c%c has zero size\n", pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
   16816            0 :       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
   16817            0 :       return pbk32->data_size;
   16818              :    } else {
   16819            0 :       BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
   16820            0 :       uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
   16821            0 :       if (size > 0xFFFF) {
   16822            0 :          printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
   16823            0 :          size = 0;
   16824              :       }
   16825            0 :       pbk->data_size = (WORD) (size);
   16826            0 :       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
   16827            0 :          printf("Warning: TID_STRUCT bank %c%c%c%c has zero size\n", pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
   16828            0 :       size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
   16829            0 :       if (size > 0xFFFF) {
   16830            0 :          printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
   16831            0 :          size = 0;
   16832              :       }
   16833            0 :       ((BANK_HEADER *) event)->data_size = size;
   16834            0 :       return pbk->data_size;
   16835              :    }
   16836              : }
   16837              : 
   16838              : /********************************************************************/
   16839              : /**
   16840              : Extract the MIDAS bank name listing of an event.
   16841              : The bklist should be dimensioned with STRING_BANKLIST_MAX
   16842              : which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
   16843              : \code
   16844              : INT adc_calib(EVENT_HEADER *pheader, void *pevent)
   16845              : {
   16846              :   INT    n_adc, nbanks;
   16847              :   WORD   *pdata;
   16848              :   char   banklist[STRING_BANKLIST_MAX];
   16849              : 
   16850              :   // Display # of banks and list of banks in the event
   16851              :   nbanks = bk_list(pevent, banklist);
   16852              :   printf("#banks:%d List:%s\n", nbanks, banklist);
   16853              : 
   16854              :   // look for ADC0 bank, return if not present
   16855              :   n_adc = bk_locate(pevent, "ADC0", &pdata);
   16856              :   ...
   16857              : }
   16858              : \endcode
   16859              : @param event pointer to current composed event
   16860              : @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
   16861              : @return number of bank found in this event.
   16862              : */
   16863            0 : INT bk_list(const void *event, char *bklist) {                               /* Full event */
   16864              :    INT nbk;
   16865            0 :    BANK *pmbk = NULL;
   16866            0 :    BANK32 *pmbk32 = NULL;
   16867            0 :    BANK32A *pmbk32a = NULL;
   16868              :    char *pdata;
   16869              : 
   16870              :    /* compose bank list */
   16871            0 :    bklist[0] = 0;
   16872            0 :    nbk = 0;
   16873              :    do {
   16874              :       /* scan all banks for bank name only */
   16875            0 :       if (bk_is32a(event)) {
   16876            0 :          bk_iterate32a(event, &pmbk32a, &pdata);
   16877            0 :          if (pmbk32a == NULL)
   16878            0 :             break;
   16879            0 :       } else if (bk_is32(event)) {
   16880            0 :          bk_iterate32(event, &pmbk32, &pdata);
   16881            0 :          if (pmbk32 == NULL)
   16882            0 :             break;
   16883              :       } else {
   16884            0 :          bk_iterate(event, &pmbk, &pdata);
   16885            0 :          if (pmbk == NULL)
   16886            0 :             break;
   16887              :       }
   16888            0 :       nbk++;
   16889              : 
   16890            0 :       if (nbk > BANKLIST_MAX) {
   16891            0 :          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
   16892            0 :          return (nbk - 1);
   16893              :       }
   16894            0 :       if (bk_is32a(event))
   16895            0 :          strncat(bklist, (char *) pmbk32a->name, 4);
   16896            0 :       else if (bk_is32(event))
   16897            0 :          strncat(bklist, (char *) pmbk32->name, 4);
   16898              :       else
   16899            0 :          strncat(bklist, (char *) pmbk->name, 4);
   16900              :    } while (1);
   16901            0 :    return (nbk);
   16902              : }
   16903              : 
   16904              : /********************************************************************/
   16905              : /**
   16906              : Locates a MIDAS bank of given name inside an event.
   16907              : @param event pointer to current composed event
   16908              : @param name bank name to look for
   16909              : @param pdata pointer to data area of bank, NULL if bank not found
   16910              : @return number of values inside the bank
   16911              : */
   16912            0 : INT bk_locate(const void *event, const char *name, void *pdata) {
   16913              :    BANK *pbk;
   16914              :    BANK32 *pbk32;
   16915              :    BANK32A *pbk32a;
   16916              :    DWORD dname;
   16917              : 
   16918            0 :    if (bk_is32a(event)) {
   16919            0 :       pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
   16920            0 :       copy_bk_name((char *) &dname, name);
   16921            0 :       while ((DWORD) ((char *) pbk32a - (char *) event) <
   16922            0 :              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
   16923            0 :          if (*((DWORD *) pbk32a->name) == dname) {
   16924            0 :             *((void **) pdata) = pbk32a + 1;
   16925            0 :             if (tid_size[pbk32a->type & 0xFF] == 0)
   16926            0 :                return pbk32a->data_size;
   16927            0 :             return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
   16928              :          }
   16929            0 :          pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
   16930              :       }
   16931            0 :    } else if (bk_is32(event)) {
   16932            0 :       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
   16933            0 :       copy_bk_name((char *) &dname, name);
   16934            0 :       while ((DWORD) ((char *) pbk32 - (char *) event) <
   16935            0 :              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
   16936            0 :          if (*((DWORD *) pbk32->name) == dname) {
   16937            0 :             *((void **) pdata) = pbk32 + 1;
   16938            0 :             if (tid_size[pbk32->type & 0xFF] == 0)
   16939            0 :                return pbk32->data_size;
   16940            0 :             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
   16941              :          }
   16942            0 :          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
   16943              :       }
   16944              :    } else {
   16945            0 :       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
   16946            0 :       copy_bk_name((char *) &dname, name);
   16947            0 :       while ((DWORD) ((char *) pbk - (char *) event) <
   16948            0 :              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
   16949            0 :          if (*((DWORD *) pbk->name) == dname) {
   16950            0 :             *((void **) pdata) = pbk + 1;
   16951            0 :             if (tid_size[pbk->type & 0xFF] == 0)
   16952            0 :                return pbk->data_size;
   16953            0 :             return pbk->data_size / tid_size[pbk->type & 0xFF];
   16954              :          }
   16955            0 :          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
   16956              :       }
   16957              : 
   16958              :    }
   16959              : 
   16960              :    /* bank not found */
   16961            0 :    *((void **) pdata) = NULL;
   16962            0 :    return 0;
   16963              : }
   16964              : 
   16965              : /********************************************************************/
   16966              : /**
   16967              : Finds a MIDAS bank of given name inside an event.
   16968              : @param pbkh pointer to current composed event
   16969              : @param name bank name to look for
   16970              : @param bklen number of elemtents in bank
   16971              : @param bktype bank type, one of TID_xxx
   16972              : @param pdata pointer to data area of bank, NULL if bank not found
   16973              : @return 1 if bank found, 0 otherwise
   16974              : */
   16975            0 : INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
   16976              :    DWORD dname;
   16977              : 
   16978            0 :    if (bk_is32a(pbkh)) {
   16979            0 :       BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
   16980            0 :       copy_bk_name((char *) &dname, name);
   16981              :       do {
   16982            0 :          if (*((DWORD *) pbk32a->name) == dname) {
   16983            0 :             *((void **) pdata) = pbk32a + 1;
   16984            0 :             if (tid_size[pbk32a->type & 0xFF] == 0)
   16985            0 :                *bklen = pbk32a->data_size;
   16986              :             else
   16987            0 :                *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
   16988              : 
   16989            0 :             *bktype = pbk32a->type;
   16990            0 :             return 1;
   16991              :          }
   16992            0 :          pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
   16993            0 :       } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
   16994            0 :    } else if (bk_is32(pbkh)) {
   16995            0 :          BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
   16996            0 :       copy_bk_name((char *) &dname, name);
   16997              :       do {
   16998            0 :          if (*((DWORD *) pbk32->name) == dname) {
   16999            0 :             *((void **) pdata) = pbk32 + 1;
   17000            0 :             if (tid_size[pbk32->type & 0xFF] == 0)
   17001            0 :                *bklen = pbk32->data_size;
   17002              :             else
   17003            0 :                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
   17004              : 
   17005            0 :             *bktype = pbk32->type;
   17006            0 :             return 1;
   17007              :          }
   17008            0 :          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
   17009            0 :       } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
   17010              :    } else {
   17011            0 :       BANK *pbk = (BANK *) (pbkh + 1);
   17012            0 :       copy_bk_name((char *) &dname, name);
   17013              :       do {
   17014            0 :          if (*((DWORD *) pbk->name) == dname) {
   17015            0 :             *((void **) pdata) = pbk + 1;
   17016            0 :             if (tid_size[pbk->type & 0xFF] == 0)
   17017            0 :                *bklen = pbk->data_size;
   17018              :             else
   17019            0 :                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
   17020              : 
   17021            0 :             *bktype = pbk->type;
   17022            0 :             return 1;
   17023              :          }
   17024            0 :          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
   17025            0 :       } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
   17026              :    }
   17027              : 
   17028              :    /* bank not found */
   17029            0 :    *((void **) pdata) = NULL;
   17030            0 :    return 0;
   17031              : }
   17032              : 
   17033              : /********************************************************************/
   17034              : /**
   17035              : Iterates through banks inside an event.
   17036              : The function can be used to enumerate all banks of an event.
   17037              : The returned pointer to the bank header has following structure:
   17038              : \code
   17039              : typedef struct {
   17040              : char   name[4];
   17041              : WORD   type;
   17042              : WORD   data_size;
   17043              : } BANK;
   17044              : \endcode
   17045              : where type is a TID_xxx value and data_size the size of the bank in bytes.
   17046              : \code
   17047              : BANK *pbk;
   17048              : INT  size;
   17049              : void *pdata;
   17050              : char name[5];
   17051              : pbk = NULL;
   17052              : do
   17053              : {
   17054              :  size = bk_iterate(event, &pbk, &pdata);
   17055              :  if (pbk == NULL)
   17056              :   break;
   17057              :  *((DWORD *)name) = *((DWORD *)(pbk)->name);
   17058              :  name[4] = 0;
   17059              :  printf("bank %s found\n", name);
   17060              : } while(TRUE);
   17061              : \endcode
   17062              : @param event Pointer to data area of event.
   17063              : @param pbk pointer to the bank header, must be NULL for the first call to
   17064              : this function.
   17065              : @param pdata Pointer to the bank header, must be NULL for the first
   17066              : call to this function
   17067              : @return Size of bank in bytes
   17068              : */
   17069            0 : INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
   17070            0 :    if (*pbk == NULL)
   17071            0 :       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
   17072              :    else
   17073            0 :       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
   17074              : 
   17075            0 :    *((void **) pdata) = (*pbk) + 1;
   17076              : 
   17077            0 :    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
   17078            0 :       *pbk = *((BANK **) pdata) = NULL;
   17079            0 :       return 0;
   17080              :    }
   17081              : 
   17082            0 :    return (*pbk)->data_size;
   17083              : }
   17084              : 
   17085              : 
   17086              : /**dox***************************************************************/
   17087              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   17088              : 
   17089              : /********************************************************************/
   17090            0 : INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
   17091              : /********************************************************************\
   17092              : 
   17093              :   Routine: bk_iterate32
   17094              : 
   17095              :   Purpose: Iterate through 32 bit MIDAS banks inside an event
   17096              : 
   17097              :   Input:
   17098              :     void   *event           pointer to the event
   17099              :     BANK32 **pbk32          must be NULL for the first call to bk_iterate
   17100              : 
   17101              :   Output:
   17102              :     BANK32 **pbk32            pointer to the bank header
   17103              :     void   *pdata           pointer to data area of the bank
   17104              : 
   17105              :   Function value:
   17106              :     INT    size of the bank in bytes
   17107              : 
   17108              : \********************************************************************/
   17109              : {
   17110            0 :    if (*pbk == NULL)
   17111            0 :       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
   17112              :    else
   17113            0 :       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
   17114              : 
   17115            0 :    *((void **) pdata) = (*pbk) + 1;
   17116              : 
   17117            0 :    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
   17118            0 :       *pbk = NULL;
   17119            0 :       pdata = NULL;
   17120            0 :       return 0;
   17121              :    }
   17122              : 
   17123            0 :    return (*pbk)->data_size;
   17124              : }
   17125              : 
   17126            0 : INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
   17127              : /********************************************************************\
   17128              : 
   17129              :   Routine: bk_iterate32a
   17130              : 
   17131              :   Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
   17132              : 
   17133              :   Input:
   17134              :     void   *event           pointer to the event
   17135              :     BANK32A **pbk32a        must be NULL for the first call to bk_iterate
   17136              : 
   17137              :   Output:
   17138              :     BANK32A **pbk32         pointer to the bank header
   17139              :     void   *pdata           pointer to data area of the bank
   17140              : 
   17141              :   Function value:
   17142              :     INT    size of the bank in bytes
   17143              : 
   17144              : \********************************************************************/
   17145              : {
   17146            0 :    if (*pbk32a == NULL)
   17147            0 :       *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
   17148              :    else
   17149            0 :       *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
   17150              : 
   17151            0 :    *((void **) pdata) = (*pbk32a) + 1;
   17152              : 
   17153            0 :    if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
   17154            0 :       *pbk32a = NULL;
   17155            0 :       pdata = NULL;
   17156            0 :       return 0;
   17157              :    }
   17158              : 
   17159            0 :    return (*pbk32a)->data_size;
   17160              : }
   17161              : 
   17162              : /**dox***************************************************************/
   17163              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   17164              : 
   17165              : /********************************************************************/
   17166              : /**
   17167              : Swaps bytes from little endian to big endian or vice versa for a whole event.
   17168              : 
   17169              : An event contains a flag which is set by bk_init() to identify
   17170              : the endian format of an event. If force is FALSE, this flag is evaluated and
   17171              : the event is only swapped if it is in the "wrong" format for this system.
   17172              : An event can be swapped to the "wrong" format on purpose for example by a
   17173              : front-end which wants to produce events in a "right" format for a back-end
   17174              : analyzer which has different byte ordering.
   17175              : @param event pointer to data area of event
   17176              : @param force If TRUE, the event is always swapped, if FALSE, the event
   17177              : is only swapped if it is in the wrong format.
   17178              : @return 1==event has been swap, 0==event has not been swapped.
   17179              : */
   17180            0 : INT bk_swap(void *event, BOOL force) {
   17181              :    BANK_HEADER *pbh;
   17182              :    BANK *pbk;
   17183              :    BANK32 *pbk32;
   17184              :    BANK32A *pbk32a;
   17185              :    void *pdata;
   17186              :    WORD type;
   17187              : 
   17188            0 :    pbh = (BANK_HEADER *) event;
   17189              : 
   17190              :    /* only swap if flags in high 16-bit */
   17191            0 :    if (pbh->flags < 0x10000 && !force)
   17192            0 :       return 0;
   17193              : 
   17194              :    /* swap bank header */
   17195            0 :    DWORD_SWAP(&pbh->data_size);
   17196            0 :    DWORD_SWAP(&pbh->flags);
   17197              : 
   17198            0 :    pbk = (BANK *) (pbh + 1);
   17199            0 :    pbk32 = (BANK32 *) pbk;
   17200            0 :    pbk32a = (BANK32A *) pbk;
   17201              : 
   17202              :    /* scan event */
   17203            0 :    while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
   17204              :       /* swap bank header */
   17205            0 :       if (bk_is32a(event)) {
   17206            0 :          DWORD_SWAP(&pbk32a->type);
   17207            0 :          DWORD_SWAP(&pbk32a->data_size);
   17208            0 :          pdata = pbk32a + 1;
   17209            0 :          type = (WORD) pbk32a->type;
   17210            0 :       } else if (bk_is32(event)) {
   17211            0 :          DWORD_SWAP(&pbk32->type);
   17212            0 :          DWORD_SWAP(&pbk32->data_size);
   17213            0 :          pdata = pbk32 + 1;
   17214            0 :          type = (WORD) pbk32->type;
   17215              :       } else {
   17216            0 :          WORD_SWAP(&pbk->type);
   17217            0 :          WORD_SWAP(&pbk->data_size);
   17218            0 :          pdata = pbk + 1;
   17219            0 :          type = pbk->type;
   17220              :       }
   17221              : 
   17222              :       /* pbk points to next bank */
   17223            0 :       if (bk_is32a(event)) {
   17224            0 :          pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
   17225            0 :          pbk = (BANK *) pbk32a;
   17226            0 :       } else if (bk_is32(event)) {
   17227            0 :          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
   17228            0 :          pbk = (BANK *) pbk32;
   17229              :       } else {
   17230            0 :          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
   17231            0 :          pbk32 = (BANK32 *) pbk;
   17232              :       }
   17233              : 
   17234            0 :       switch (type) {
   17235            0 :          case TID_UINT16:
   17236              :          case TID_INT16:
   17237            0 :             while ((char *) pdata < (char *) pbk) {
   17238            0 :                WORD_SWAP(pdata);
   17239            0 :                pdata = (void *) (((WORD *) pdata) + 1);
   17240              :             }
   17241            0 :             break;
   17242              : 
   17243            0 :          case TID_UINT32:
   17244              :          case TID_INT32:
   17245              :          case TID_BOOL:
   17246              :          case TID_FLOAT:
   17247            0 :             while ((char *) pdata < (char *) pbk) {
   17248            0 :                DWORD_SWAP(pdata);
   17249            0 :                pdata = (void *) (((DWORD *) pdata) + 1);
   17250              :             }
   17251            0 :             break;
   17252              : 
   17253            0 :          case TID_DOUBLE:
   17254              :          case TID_INT64:
   17255              :          case TID_UINT64:
   17256            0 :             while ((char *) pdata < (char *) pbk) {
   17257            0 :                QWORD_SWAP(pdata);
   17258            0 :                pdata = (void *) (((double *) pdata) + 1);
   17259              :             }
   17260            0 :             break;
   17261              :       }
   17262              :    }
   17263              : 
   17264            0 :    return CM_SUCCESS;
   17265              : }
   17266              : 
   17267              : /**dox***************************************************************/
   17268              : 
   17269              : /** @} *//* end of bkfunctionc */
   17270              : 
   17271              : 
   17272              : /**dox***************************************************************/
   17273              : /** @addtogroup rbfunctionc
   17274              :  *
   17275              :  *  @{  */
   17276              : 
   17277              : /**dox***************************************************************/
   17278              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
   17279              : /********************************************************************/
   17280              : 
   17281              : /********************************************************************\
   17282              : *                                                                    *
   17283              : *                 Ring buffer functions                              *
   17284              : *                                                                    *
   17285              : * Provide an inter-thread buffer scheme for handling front-end       *
   17286              : * events. This code allows concurrent data acquisition, calibration  *
   17287              : * and network transfer on a multi-CPU machine. One thread reads      *
   17288              : * out the data, passes it vis the ring buffer functions              *
   17289              : * to another thread running on the other CPU, which can then         *
   17290              : * calibrate and/or send the data over the network.                   *
   17291              : *                                                                    *
   17292              : \********************************************************************/
   17293              : 
   17294              : typedef struct {
   17295              :    unsigned char *buffer;
   17296              :    unsigned int size;
   17297              :    unsigned int max_event_size;
   17298              :    unsigned char *rp;
   17299              :    unsigned char *wp;
   17300              :    unsigned char *ep;
   17301              : } RING_BUFFER;
   17302              : 
   17303              : #define MAX_RING_BUFFER 100
   17304              : 
   17305              : static RING_BUFFER rb[MAX_RING_BUFFER];
   17306              : 
   17307              : static volatile int _rb_nonblocking = 0;
   17308              : 
   17309              : /**dox***************************************************************/
   17310              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
   17311              : 
   17312              : /********************************************************************/
   17313              : /**
   17314              : Set all rb_get_xx to nonblocking. Needed in multi-thread
   17315              : environments for stopping all theads without deadlock
   17316              : @return DB_SUCCESS
   17317              : */
   17318            0 : int rb_set_nonblocking()
   17319              : /********************************************************************\
   17320              : 
   17321              :   Routine: rb_set_nonblocking
   17322              : 
   17323              :   Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
   17324              :            environments for stopping all theads without deadlock
   17325              : 
   17326              :   Input:
   17327              :     NONE
   17328              : 
   17329              :   Output:
   17330              :     NONE
   17331              : 
   17332              :   Function value:
   17333              :     DB_SUCCESS       Successful completion
   17334              : 
   17335              : \********************************************************************/
   17336              : {
   17337            0 :    _rb_nonblocking = 1;
   17338              : 
   17339            0 :    return DB_SUCCESS;
   17340              : }
   17341              : 
   17342              : /********************************************************************/
   17343              : /**
   17344              : Create a ring buffer with a given size
   17345              : 
   17346              : Provide an inter-thread buffer scheme for handling front-end
   17347              : events. This code allows concurrent data acquisition, calibration
   17348              : and network transfer on a multi-CPU machine. One thread reads
   17349              : out the data, passes it via the ring buffer functions
   17350              : to another thread running on the other CPU, which can then
   17351              : calibrate and/or send the data over the network.
   17352              : 
   17353              : @param size             Size of ring buffer, must be larger than
   17354              :                          2*max_event_size
   17355              : @param max_event_size   Maximum event size to be placed into
   17356              : @param *handle          Handle to ring buffer
   17357              : @return DB_SUCCESS, DB_NO_MEMORY, DB_INVALID_PARAM
   17358              : */
   17359            0 : int rb_create(int size, int max_event_size, int *handle)
   17360              : /********************************************************************\
   17361              : 
   17362              :   Routine: rb_create
   17363              : 
   17364              :   Purpose: Create a ring buffer with a given size
   17365              : 
   17366              :   Input:
   17367              :     int size             Size of ring buffer, must be larger than
   17368              :                          2*max_event_size
   17369              :     int max_event_size   Maximum event size to be placed into
   17370              :                          ring buffer
   17371              :   Output:
   17372              :     int *handle          Handle to ring buffer
   17373              : 
   17374              :   Function value:
   17375              :     DB_SUCCESS           Successful completion
   17376              :     DB_NO_MEMORY         Maximum number of ring buffers exceeded
   17377              :     DB_INVALID_PARAM     Invalid event size specified
   17378              : 
   17379              : \********************************************************************/
   17380              : {
   17381              :    int i;
   17382              : 
   17383            0 :    for (i = 0; i < MAX_RING_BUFFER; i++)
   17384            0 :       if (rb[i].buffer == NULL)
   17385            0 :          break;
   17386              : 
   17387            0 :    if (i == MAX_RING_BUFFER)
   17388            0 :       return DB_NO_MEMORY;
   17389              : 
   17390            0 :    if (size < max_event_size * 2)
   17391            0 :       return DB_INVALID_PARAM;
   17392              : 
   17393            0 :    memset(&rb[i], 0, sizeof(RING_BUFFER));
   17394            0 :    rb[i].buffer = (unsigned char *) M_MALLOC(size);
   17395            0 :    assert(rb[i].buffer);
   17396            0 :    rb[i].size = size;
   17397            0 :    rb[i].max_event_size = max_event_size;
   17398            0 :    rb[i].rp = rb[i].buffer;
   17399            0 :    rb[i].wp = rb[i].buffer;
   17400            0 :    rb[i].ep = rb[i].buffer;
   17401              : 
   17402            0 :    *handle = i + 1;
   17403              : 
   17404            0 :    return DB_SUCCESS;
   17405              : }
   17406              : 
   17407              : /********************************************************************/
   17408              : /**
   17409              : Delete a ring buffer
   17410              : @param handle  Handle of the ring buffer
   17411              : @return  DB_SUCCESS
   17412              : */
   17413            0 : int rb_delete(int handle)
   17414              : /********************************************************************\
   17415              : 
   17416              :   Routine: rb_delete
   17417              : 
   17418              :   Purpose: Delete a ring buffer
   17419              : 
   17420              :   Input:
   17421              :     none
   17422              :   Output:
   17423              :     int handle       Handle to ring buffer
   17424              : 
   17425              :   Function value:
   17426              :     DB_SUCCESS       Successful completion
   17427              : 
   17428              : \********************************************************************/
   17429              : {
   17430            0 :    if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
   17431            0 :       return DB_INVALID_HANDLE;
   17432              : 
   17433            0 :    M_FREE(rb[handle - 1].buffer);
   17434            0 :    rb[handle - 1].buffer = NULL;
   17435            0 :    memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
   17436              : 
   17437            0 :    return DB_SUCCESS;
   17438              : }
   17439              : 
   17440              : /********************************************************************/
   17441              : /**
   17442              : Retrieve write pointer where new data can be written
   17443              : @param handle               Ring buffer handle
   17444              : @param millisec             Optional timeout in milliseconds if
   17445              :                               buffer is full. Zero to not wait at
   17446              :                               all (non-blocking)
   17447              : @param  **p                  Write pointer
   17448              : @return DB_SUCCESS, DB_TIMEOUT, DB_INVALID_HANDLE
   17449              : */
   17450            0 : int rb_get_wp(int handle, void **p, int millisec)
   17451              : /********************************************************************\
   17452              : 
   17453              : Routine: rb_get_wp
   17454              : 
   17455              :   Purpose: Retrieve write pointer where new data can be written
   17456              : 
   17457              :   Input:
   17458              :      int handle               Ring buffer handle
   17459              :      int millisec             Optional timeout in milliseconds if
   17460              :                               buffer is full. Zero to not wait at
   17461              :                               all (non-blocking)
   17462              : 
   17463              :   Output:
   17464              :     char **p                  Write pointer
   17465              : 
   17466              :   Function value:
   17467              :     DB_SUCCESS       Successful completion
   17468              : 
   17469              : \********************************************************************/
   17470              : {
   17471              :    int h, i;
   17472              :    unsigned char *rp;
   17473              : 
   17474            0 :    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
   17475            0 :       return DB_INVALID_HANDLE;
   17476              : 
   17477            0 :    h = handle - 1;
   17478              : 
   17479            0 :    for (i = 0; i <= millisec / 10; i++) {
   17480              : 
   17481            0 :       rp = rb[h].rp;            // keep local copy for convenience
   17482              : 
   17483              :       /* check if enough size for wp >= rp without wrap-around */
   17484            0 :       if (rb[h].wp >= rp
   17485            0 :           && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
   17486            0 :          *p = rb[h].wp;
   17487            0 :          return DB_SUCCESS;
   17488              :       }
   17489              : 
   17490              :       /* check if enough size for wp >= rp with wrap-around */
   17491            0 :       if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
   17492            0 :           rp > rb[h].buffer) {    // next increment of wp wraps around, so need space at beginning
   17493            0 :          *p = rb[h].wp;
   17494            0 :          return DB_SUCCESS;
   17495              :       }
   17496              : 
   17497              :       /* check if enough size for wp < rp */
   17498            0 :       if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
   17499            0 :          *p = rb[h].wp;
   17500            0 :          return DB_SUCCESS;
   17501              :       }
   17502              : 
   17503            0 :       if (millisec == 0)
   17504            0 :          return DB_TIMEOUT;
   17505              : 
   17506            0 :       if (_rb_nonblocking)
   17507            0 :          return DB_TIMEOUT;
   17508              : 
   17509              :       /* wait one time slice */
   17510            0 :       ss_sleep(10);
   17511              :    }
   17512              : 
   17513            0 :    return DB_TIMEOUT;
   17514              : }
   17515              : 
   17516              : /********************************************************************/
   17517              : /** rb_increment_wp
   17518              : 
   17519              : Increment current write pointer, making the data at
   17520              : the write pointer available to the receiving thread
   17521              : @param handle               Ring buffer handle
   17522              : @param size                 Number of bytes placed at the WP
   17523              : @return DB_SUCCESS, DB_INVALID_PARAM, DB_INVALID_HANDLE
   17524              : */
   17525            0 : int rb_increment_wp(int handle, int size)
   17526              : /********************************************************************\
   17527              : 
   17528              :   Routine: rb_increment_wp
   17529              : 
   17530              :   Purpose: Increment current write pointer, making the data at
   17531              :            the write pointer available to the receiving thread
   17532              : 
   17533              :   Input:
   17534              :      int handle               Ring buffer handle
   17535              :      int size                 Number of bytes placed at the WP
   17536              : 
   17537              :   Output:
   17538              :     NONE
   17539              : 
   17540              :   Function value:
   17541              :     DB_SUCCESS                Successful completion
   17542              :     DB_INVALID_PARAM          Event size too large or invalid handle
   17543              : \********************************************************************/
   17544              : {
   17545              :    int h;
   17546              :    unsigned char *new_wp;
   17547              : 
   17548            0 :    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
   17549            0 :       return DB_INVALID_HANDLE;
   17550              : 
   17551            0 :    h = handle - 1;
   17552              : 
   17553            0 :    if ((DWORD) size > rb[h].max_event_size) {
   17554            0 :       cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
   17555            0 :              size/1024/1024, rb[h].max_event_size/1024/1024);
   17556            0 :       abort();
   17557              :    }
   17558              : 
   17559            0 :    new_wp = rb[h].wp + size;
   17560              : 
   17561              :    /* wrap around wp if not enough space */
   17562            0 :    if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
   17563            0 :       rb[h].ep = new_wp;
   17564            0 :       new_wp = rb[h].buffer;
   17565            0 :       assert(rb[h].rp != rb[h].buffer);
   17566              :    } else
   17567            0 :       if (new_wp > rb[h].ep)
   17568            0 :          rb[h].ep = new_wp;
   17569              : 
   17570            0 :    rb[h].wp = new_wp;
   17571              : 
   17572            0 :    return DB_SUCCESS;
   17573              : }
   17574              : 
   17575              : /********************************************************************/
   17576              : /**
   17577              : Obtain the current read pointer at which new data is
   17578              : available with optional timeout
   17579              : 
   17580              : @param  handle               Ring buffer handle
   17581              : @param  millisec             Optional timeout in milliseconds if
   17582              :                              buffer is full. Zero to not wait at
   17583              :                              all (non-blocking)
   17584              : 
   17585              : @param **p                 Address of pointer pointing to newly
   17586              :                              available data. If p == NULL, only
   17587              :                              return status.
   17588              : @return  DB_SUCCESS, DB_TIEMOUT, DB_INVALID_HANDLE
   17589              : 
   17590              : */
   17591            0 : int rb_get_rp(int handle, void **p, int millisec)
   17592              : /********************************************************************\
   17593              : 
   17594              :   Routine: rb_get_rp
   17595              : 
   17596              :   Purpose: Obtain the current read pointer at which new data is
   17597              :            available with optional timeout
   17598              : 
   17599              :   Input:
   17600              :     int handle               Ring buffer handle
   17601              :     int millisec             Optional timeout in milliseconds if
   17602              :                              buffer is full. Zero to not wait at
   17603              :                              all (non-blocking)
   17604              : 
   17605              :   Output:
   17606              :     char **p                 Address of pointer pointing to newly
   17607              :                              available data. If p == NULL, only
   17608              :                              return status.
   17609              : 
   17610              :   Function value:
   17611              :     DB_SUCCESS       Successful completion
   17612              : 
   17613              : \********************************************************************/
   17614              : {
   17615              :    int i, h;
   17616              : 
   17617            0 :    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
   17618            0 :       return DB_INVALID_HANDLE;
   17619              : 
   17620            0 :    h = handle - 1;
   17621              : 
   17622            0 :    for (i = 0; i <= millisec / 10; i++) {
   17623              : 
   17624            0 :       if (rb[h].wp != rb[h].rp) {
   17625            0 :          if (p != NULL)
   17626            0 :             *p = rb[handle - 1].rp;
   17627            0 :          return DB_SUCCESS;
   17628              :       }
   17629              : 
   17630            0 :       if (millisec == 0)
   17631            0 :          return DB_TIMEOUT;
   17632              : 
   17633            0 :       if (_rb_nonblocking)
   17634            0 :          return DB_TIMEOUT;
   17635              : 
   17636              :       /* wait one time slice */
   17637            0 :       ss_sleep(10);
   17638              :    }
   17639              : 
   17640            0 :    return DB_TIMEOUT;
   17641              : }
   17642              : 
   17643              : /********************************************************************/
   17644              : /**
   17645              : Increment current read pointer, freeing up space for the writing thread.
   17646              : 
   17647              : @param handle               Ring buffer handle
   17648              : @param size                 Number of bytes to free up at current
   17649              :                               read pointer
   17650              : @return  DB_SUCCESS, DB_INVALID_PARAM
   17651              : 
   17652              : */
   17653            0 : int rb_increment_rp(int handle, int size)
   17654              : /********************************************************************\
   17655              : 
   17656              :   Routine: rb_increment_rp
   17657              : 
   17658              :   Purpose: Increment current read pointer, freeing up space for
   17659              :            the writing thread.
   17660              : 
   17661              :   Input:
   17662              :      int handle               Ring buffer handle
   17663              :      int size                 Number of bytes to free up at current
   17664              :                               read pointer
   17665              : 
   17666              :   Output:
   17667              :     NONE
   17668              : 
   17669              :   Function value:
   17670              :     DB_SUCCESS                Successful completion
   17671              :     DB_INVALID_PARAM          Event size too large or invalid handle
   17672              : 
   17673              : \********************************************************************/
   17674              : {
   17675              :    int h;
   17676              : 
   17677              :    unsigned char *new_rp;
   17678              :    unsigned char *ep;
   17679              : 
   17680            0 :    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
   17681            0 :       return DB_INVALID_HANDLE;
   17682              : 
   17683            0 :    h = handle - 1;
   17684              : 
   17685            0 :    if ((DWORD) size > rb[h].max_event_size)
   17686            0 :       return DB_INVALID_PARAM;
   17687              : 
   17688            0 :    new_rp = rb[h].rp + size;
   17689            0 :    ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
   17690              : 
   17691              :    /* wrap around if end pointer reached */
   17692            0 :    if (new_rp >= ep && rb[h].wp < ep)
   17693            0 :       new_rp = rb[h].buffer;
   17694              : 
   17695            0 :    rb[handle - 1].rp = new_rp;
   17696              : 
   17697            0 :    return DB_SUCCESS;
   17698              : }
   17699              : 
   17700              : /********************************************************************/
   17701              : /**
   17702              : Return number of bytes in a ring buffer
   17703              : 
   17704              : @param handle              Handle of the buffer to get the info
   17705              : @param *n_bytes            Number of bytes in buffer
   17706              : @return DB_SUCCESS, DB_INVALID_HANDLE
   17707              : */
   17708            0 : int rb_get_buffer_level(int handle, int *n_bytes)
   17709              : /********************************************************************\
   17710              : 
   17711              :   Routine: rb_get_buffer_level
   17712              : 
   17713              :   Purpose: Return number of bytes in a ring buffer
   17714              : 
   17715              :   Input:
   17716              :     int handle              Handle of the buffer to get the info
   17717              : 
   17718              :   Output:
   17719              :     int *n_bytes            Number of bytes in buffer
   17720              : 
   17721              :   Function value:
   17722              :     DB_SUCCESS              Successful completion
   17723              :     DB_INVALID_HANDLE       Buffer handle is invalid
   17724              : 
   17725              : \********************************************************************/
   17726              : {
   17727              :    int h;
   17728              : 
   17729            0 :    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
   17730            0 :       return DB_INVALID_HANDLE;
   17731              : 
   17732            0 :    h = handle - 1;
   17733              : 
   17734            0 :    if (rb[h].wp >= rb[h].rp)
   17735            0 :       *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
   17736              :    else
   17737            0 :       *n_bytes =
   17738            0 :               (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
   17739              : 
   17740            0 :    return DB_SUCCESS;
   17741              : }
   17742              : 
   17743              : /** @} *//* end of rbfunctionc */
   17744              : 
   17745              : 
   17746            0 : int cm_write_event_to_odb(HNDLE hDB, HNDLE hKey, const EVENT_HEADER* pevent, INT format)
   17747              : {
   17748            0 :    if (format == FORMAT_FIXED) {
   17749              :       int status;
   17750            0 :       status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
   17751            0 :       if (status != DB_SUCCESS) {
   17752            0 :          cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
   17753            0 :          return status;
   17754              :       }
   17755            0 :       return SUCCESS;
   17756            0 :    } else if (format == FORMAT_MIDAS) {
   17757              :       INT size, i, status, n_data;
   17758              :       int n;
   17759              :       char *pdata, *pdata0;
   17760              : 
   17761              :       char name[5];
   17762              :       BANK_HEADER *pbh;
   17763              :       BANK *pbk;
   17764              :       BANK32 *pbk32;
   17765              :       BANK32A *pbk32a;
   17766              :       DWORD bkname;
   17767              :       WORD bktype;
   17768              :       HNDLE hKeyRoot, hKeyl, *hKeys;
   17769              :       KEY key;
   17770              : 
   17771            0 :       pbh = (BANK_HEADER *) (pevent + 1);
   17772            0 :       pbk = NULL;
   17773            0 :       pbk32 = NULL;
   17774            0 :       pbk32a = NULL;
   17775              : 
   17776              :       /* count number of banks */
   17777            0 :       for (n=0 ; ; n++) {
   17778            0 :          if (bk_is32a(pbh)) {
   17779            0 :             bk_iterate32a(pbh, &pbk32a, &pdata);
   17780            0 :             if (pbk32a == NULL)
   17781            0 :                break;
   17782            0 :          } else if (bk_is32(pbh)) {
   17783            0 :             bk_iterate32(pbh, &pbk32, &pdata);
   17784            0 :             if (pbk32 == NULL)
   17785            0 :                break;
   17786              :          } else {
   17787            0 :             bk_iterate(pbh, &pbk, &pdata);
   17788            0 :             if (pbk == NULL)
   17789            0 :                break;
   17790              :          }
   17791              :       }
   17792              : 
   17793              :       /* build array of keys */
   17794            0 :       hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
   17795              : 
   17796            0 :       pbk = NULL;
   17797            0 :       pbk32 = NULL;
   17798            0 :       n = 0;
   17799              :       do {
   17800              :          /* scan all banks */
   17801            0 :          if (bk_is32a(pbh)) {
   17802            0 :             size = bk_iterate32a(pbh, &pbk32a, &pdata);
   17803            0 :             if (pbk32a == NULL)
   17804            0 :                break;
   17805            0 :             bkname = *((DWORD *) pbk32a->name);
   17806            0 :             bktype = (WORD) pbk32a->type;
   17807            0 :          } else if (bk_is32(pbh)) {
   17808            0 :             size = bk_iterate32(pbh, &pbk32, &pdata);
   17809            0 :             if (pbk32 == NULL)
   17810            0 :                break;
   17811            0 :             bkname = *((DWORD *) pbk32->name);
   17812            0 :             bktype = (WORD) pbk32->type;
   17813              :          } else {
   17814            0 :             size = bk_iterate(pbh, &pbk, &pdata);
   17815            0 :             if (pbk == NULL)
   17816            0 :                break;
   17817            0 :             bkname = *((DWORD *) pbk->name);
   17818            0 :             bktype = (WORD) pbk->type;
   17819              :          }
   17820              : 
   17821            0 :          n_data = size;
   17822            0 :          if (rpc_tid_size(bktype & 0xFF))
   17823            0 :             n_data /= rpc_tid_size(bktype & 0xFF);
   17824              : 
   17825              :          /* get bank key */
   17826            0 :          *((DWORD *) name) = bkname;
   17827            0 :          name[4] = 0;
   17828              :          /* record the start of the data in case it is struct */
   17829            0 :          pdata0 = pdata;
   17830            0 :          if (bktype == TID_STRUCT) {
   17831            0 :             status = db_find_key(hDB, hKey, name, &hKeyRoot);
   17832            0 :             if (status != DB_SUCCESS) {
   17833            0 :                cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
   17834            0 :                continue;
   17835              :             }
   17836              : 
   17837              :             /* write structured bank */
   17838            0 :             for (i = 0;; i++) {
   17839            0 :                status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
   17840            0 :                if (status == DB_NO_MORE_SUBKEYS)
   17841            0 :                   break;
   17842              : 
   17843            0 :                db_get_key(hDB, hKeyl, &key);
   17844              : 
   17845              :                /* adjust for alignment */
   17846            0 :                if (key.type != TID_STRING && key.type != TID_LINK)
   17847            0 :                   pdata = (pdata0 + VALIGN(pdata-pdata0, MIN(ss_get_struct_align(), key.item_size)));
   17848              : 
   17849            0 :                status = db_set_data1(hDB, hKeyl, pdata, key.item_size * key.num_values, key.num_values, key.type);
   17850            0 :                if (status != DB_SUCCESS) {
   17851            0 :                   cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
   17852            0 :                   continue;
   17853              :                }
   17854            0 :                hKeys[n++] = hKeyl;
   17855              : 
   17856              :                /* shift data pointer to next item */
   17857            0 :                pdata += key.item_size * key.num_values;
   17858              :             }
   17859              :          } else {
   17860              :             /* write variable length bank  */
   17861            0 :             status = db_find_key(hDB, hKey, name, &hKeyRoot);
   17862            0 :             if (status != DB_SUCCESS) {
   17863            0 :                status = db_create_key(hDB, hKey, name, bktype);
   17864            0 :                if (status != DB_SUCCESS) {
   17865            0 :                   cm_msg(MERROR, "cm_write_event_to_odb", "cannot create key for bank \"%s\" with tid %d in ODB, db_create_key() status %d", name, bktype, status);
   17866            0 :                   continue;
   17867              :                }
   17868            0 :                status = db_find_key(hDB, hKey, name, &hKeyRoot);
   17869            0 :                if (status != DB_SUCCESS) {
   17870            0 :                   cm_msg(MERROR, "cm_write_event_to_odb", "cannot find key for bank \"%s\" in ODB, after db_create_key(), db_find_key() status %d", name, status);
   17871            0 :                   continue;
   17872              :                }
   17873              :             }
   17874            0 :             if (n_data > 0) {
   17875            0 :                status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
   17876            0 :                if (status != DB_SUCCESS) {
   17877            0 :                   cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
   17878              :                }
   17879            0 :                hKeys[n++] = hKeyRoot;
   17880              :             }
   17881              :          }
   17882              :       } while (1);
   17883              : 
   17884              :       /* notify all hot-lined clients in one go */
   17885            0 :       db_notify_clients_array(hDB, hKeys, n*sizeof(INT));
   17886              : 
   17887            0 :       free(hKeys);
   17888              : 
   17889            0 :       return SUCCESS;
   17890              :    } else {
   17891            0 :       cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
   17892            0 :       return CM_DB_ERROR;
   17893              :    }
   17894              : }
   17895              : 
   17896              : /* emacs
   17897              :  * Local Variables:
   17898              :  * tab-width: 8
   17899              :  * c-basic-offset: 3
   17900              :  * indent-tabs-mode: nil
   17901              :  * End:
   17902              :  */
        

Generated by: LCOV version 2.0-1