midas.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 
00003   Name:         MIDAS.C
00004   Created by:   Stefan Ritt
00005 
00006   Contents:     MIDAS main library funcitons
00007 
00008   $Id: midas.c 4825 2010-09-17 18:36:20Z olchanski $
00009 
00010 \********************************************************************/
00011 
00012 #include "midas.h"
00013 #include "msystem.h"
00014 #include "strlcpy.h"
00015 #include <assert.h>
00016 #include <signal.h>
00017 
00018 /* SVN revision number. This value will be changed by the Subversion
00019    system automatically on every revision of midas.c. It will be
00020    returned by cm_get_revision(), for example in the odbedit "ver"
00021    command */
00022 char *svn_revision = "$Rev: 4825 $";
00023 
00024 /**dox***************************************************************/
00025 /** @file midas.c
00026 The main core C-code for Midas.
00027 */
00028 
00029 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00030  */
00031 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00032  */
00033 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00034  */
00035 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00036  */
00037 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00038  */
00039 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00040  */
00041 /** @defgroup rbfunctionc Midas Ring Buffer Functions (rb_xxx)
00042  */
00043 
00044 /**dox***************************************************************/
00045 /** @addtogroup midasincludecode
00046  *
00047  *  @{  */
00048 
00049 /**dox***************************************************************/
00050 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00051 
00052 /********************************************************************/
00053 /* data type sizes */
00054 INT tid_size[] = {
00055    0,                           /* tid == 0 not defined                               */
00056    1,                           /* TID_BYTE      unsigned byte         0       255    */
00057    1,                           /* TID_SBYTE     signed byte         -128      127    */
00058    1,                           /* TID_CHAR      single character      0       255    */
00059    2,                           /* TID_WORD      two bytes             0      65535   */
00060    2,                           /* TID_SHORT     signed word        -32768    32767   */
00061    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00062    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00063    4,                           /* TID_BOOL      four bytes bool       0        1     */
00064    4,                           /* TID_FLOAT     4 Byte float format                  */
00065    8,                           /* TID_DOUBLE    8 Byte float format                  */
00066    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00067    0,                           /* TID_STRING    zero terminated string               */
00068    0,                           /* TID_ARRAY     variable length array of unkown type */
00069    0,                           /* TID_STRUCT    C structure                          */
00070    0,                           /* TID_KEY       key in online database               */
00071    0                            /* TID_LINK      link in online database              */
00072 };
00073 
00074 /* data type names */
00075 char *tid_name[] = {
00076    "NULL",
00077    "BYTE",
00078    "SBYTE",
00079    "CHAR",
00080    "WORD",
00081    "SHORT",
00082    "DWORD",
00083    "INT",
00084    "BOOL",
00085    "FLOAT",
00086    "DOUBLE",
00087    "BITFIELD",
00088    "STRING",
00089    "ARRAY",
00090    "STRUCT",
00091    "KEY",
00092    "LINK"
00093 };
00094 
00095 struct {
00096    int transition;
00097    char name[32];
00098 } trans_name[] = {
00099    {
00100    TR_START, "START",}, {
00101    TR_STOP, "STOP",}, {
00102    TR_PAUSE, "PAUSE",}, {
00103    TR_RESUME, "RESUME",}, {
00104    TR_STARTABORT, "STARTABORT",}, {
00105    TR_DEFERRED, "DEFERRED",}, {
00106 0, "",},};
00107 
00108 /* Globals */
00109 #ifdef OS_MSDOS
00110 extern unsigned _stklen = 60000U;
00111 #endif
00112 
00113 extern DATABASE *_database;
00114 extern INT _database_entries;
00115 
00116 static BUFFER *_buffer;
00117 static INT _buffer_entries = 0;
00118 
00119 static INT _msg_buffer = 0;
00120 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00121 
00122 static REQUEST_LIST *_request_list;
00123 static INT _request_list_entries = 0;
00124 
00125 static EVENT_HEADER *_event_buffer;
00126 static INT _event_buffer_size = 0;
00127 
00128 static char *_net_recv_buffer;
00129 static INT _net_recv_buffer_size = 0;
00130 static INT _net_recv_buffer_size_odb = 0;
00131 
00132 static char *_net_send_buffer;
00133 static INT _net_send_buffer_size = 0;
00134 
00135 static char *_tcp_buffer = NULL;
00136 static INT _tcp_wp = 0;
00137 static INT _tcp_rp = 0;
00138 static INT _rpc_sock = 0;
00139 static MUTEX_T *_mutex_rpc = NULL;
00140 
00141 static void (*_debug_print) (char *) = NULL;
00142 static INT _debug_mode = 0;
00143 
00144 static INT _watchdog_last_called = 0;
00145 
00146 int _rpc_connect_timeout = 10000;
00147 
00148 /* table for transition functions */
00149 
00150 typedef struct {
00151    INT transition;
00152    INT sequence_number;
00153     INT(*func) (INT, char *);
00154 } TRANS_TABLE;
00155 
00156 #define MAX_TRANSITIONS 20
00157 
00158 TRANS_TABLE _trans_table[MAX_TRANSITIONS];
00159 
00160 TRANS_TABLE _deferred_trans_table[] = {
00161    {TR_START, 0, NULL},
00162    {TR_STOP, 0, NULL},
00163    {TR_PAUSE, 0, NULL},
00164    {TR_RESUME, 0, NULL},
00165    {0, 0, NULL}
00166 };
00167 
00168 static BOOL _server_registered = FALSE;
00169 
00170 static INT rpc_transition_dispatch(INT idx, void *prpc_param[]);
00171 
00172 void cm_ctrlc_handler(int sig);
00173 
00174 typedef struct {
00175    INT code;
00176    char *string;
00177 } ERROR_TABLE;
00178 
00179 ERROR_TABLE _error_table[] = {
00180    {CM_WRONG_PASSWORD, "Wrong password"},
00181    {CM_UNDEF_EXP, "Experiment not defined"},
00182    {CM_UNDEF_ENVIRON,
00183     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00184    {RPC_NET_ERROR, "Cannot connect to remote host"},
00185    {0, NULL}
00186 };
00187 
00188 
00189 typedef struct {
00190    void *adr;
00191    int size;
00192    char file[80];
00193    int line;
00194 } DBG_MEM_LOC;
00195 
00196 DBG_MEM_LOC *_mem_loc = NULL;
00197 INT _n_mem = 0;
00198 
00199 void *dbg_malloc(unsigned int size, char *file, int line)
00200 {
00201    FILE *f;
00202    void *adr;
00203    int i;
00204 
00205    adr = malloc(size);
00206 
00207    /* search for deleted entry */
00208    for (i = 0; i < _n_mem; i++)
00209       if (_mem_loc[i].adr == NULL)
00210          break;
00211 
00212    if (i == _n_mem) {
00213       _n_mem++;
00214       if (!_mem_loc)
00215          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00216       else
00217          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00218    }
00219 
00220    _mem_loc[i].adr = adr;
00221    _mem_loc[i].size = size;
00222    strcpy(_mem_loc[i].file, file);
00223    _mem_loc[i].line = line;
00224 
00225    f = fopen("mem.txt", "w");
00226    for (i = 0; i < _n_mem; i++)
00227       if (_mem_loc[i].adr)
00228          fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line, _mem_loc[i].size,
00229                  _mem_loc[i].adr);
00230    fclose(f);
00231 
00232    return adr;
00233 }
00234 
00235 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00236 {
00237    void *adr;
00238 
00239    adr = dbg_malloc(size * count, file, line);
00240    if (adr)
00241       memset(adr, 0, size * count);
00242 
00243    return adr;
00244 }
00245 
00246 void dbg_free(void *adr, char *file, int line)
00247 {
00248    FILE *f;
00249    int i;
00250 
00251    free(adr);
00252 
00253    for (i = 0; i < _n_mem; i++)
00254       if (_mem_loc[i].adr == adr)
00255          break;
00256 
00257    if (i < _n_mem)
00258       _mem_loc[i].adr = NULL;
00259 
00260    f = fopen("mem.txt", "w");
00261    for (i = 0; i < _n_mem; i++)
00262       if (_mem_loc[i].adr)
00263          fprintf(f, "%s:%d %s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
00264                  file, line, _mem_loc[i].size, _mem_loc[i].adr);
00265    fclose(f);
00266 }
00267 
00268 /********************************************************************/
00269 static int resize_net_send_buffer(const char* from, int new_size)
00270 /********************************************************************/
00271 {
00272    if (new_size <= _net_send_buffer_size)
00273       return SUCCESS;
00274 
00275    _net_send_buffer = (char *) realloc(_net_send_buffer, new_size);
00276    if (_net_send_buffer == NULL) {
00277       cm_msg(MERROR, from, "Cannot allocate %d bytes for network buffer", new_size);
00278       return RPC_EXCEED_BUFFER;
00279    }
00280 
00281    //printf("reallocate_net_send_buffer %p size %d->%d from %s\n", _net_send_buffer, _net_send_buffer_size, new_size, from);
00282    _net_send_buffer_size = new_size;
00283    return SUCCESS;
00284 }
00285 
00286 /********************************************************************\
00287 *                                                                    *
00288 *              Common message functions                              *
00289 *                                                                    *
00290 \********************************************************************/
00291 
00292 static int (*_message_print) (const char *) = puts;
00293 static INT _message_mask_system = MT_ALL;
00294 static INT _message_mask_user = MT_ALL;
00295 
00296 
00297 /**dox***************************************************************/
00298 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00299 
00300 /**dox***************************************************************/
00301 /** @addtogroup msgfunctionc
00302  *
00303  *  @{  */
00304 
00305 /********************************************************************/
00306 /**
00307 Convert error code to string. Used after cm_connect_experiment to print
00308 error string in command line programs or windows programs.
00309 @param code Error code as defined in midas.h
00310 @param string Error string
00311 @return CM_SUCCESS
00312 */
00313 INT cm_get_error(INT code, char *string)
00314 {
00315    INT i;
00316 
00317    for (i = 0; _error_table[i].code; i++)
00318       if (_error_table[i].code == code) {
00319          strcpy(string, _error_table[i].string);
00320          return CM_SUCCESS;
00321       }
00322 
00323    sprintf(string, "Unexpected error #%d", code);
00324    return CM_SUCCESS;
00325 }
00326 
00327 /********************************************************************/
00328 /**
00329 Set message masks. When a message is generated by calling cm_msg(),
00330 it can got to two destinatinons. First a user defined callback routine
00331 and second to the "SYSMSG" buffer.
00332 
00333 A user defined callback receives all messages which satisfy the user_mask.
00334 
00335 \code
00336 int message_print(const char *msg)
00337 {
00338   char str[160];
00339 
00340   memset(str, ' ', 159);
00341   str[159] = 0;
00342   if (msg[0] == '[')
00343     msg = strchr(msg, ']')+2;
00344   memcpy(str, msg, strlen(msg));
00345   ss_printf(0, 20, str);
00346   return 0;
00347 }
00348 ...
00349   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
00350 ...
00351 \endcode
00352 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
00353 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
00354 @param func Function which receives all printout. By setting "puts",
00355        messages are just printed to the screen.
00356 @return CM_SUCCESS
00357 */
00358 INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func) (const char *))
00359 {
00360    _message_mask_system = system_mask;
00361    _message_mask_user = user_mask;
00362    _message_print = func;
00363 
00364    return BM_SUCCESS;
00365 }
00366 
00367 /********************************************************************/
00368 /**
00369 Write message to logging file. Called by cm_msg.
00370 @attention May burn your fingers
00371 @param message_type      Message type
00372 @param message          Message string
00373 @return CM_SUCCESS
00374 */
00375 INT cm_msg_log(INT message_type, const char *message)
00376 {
00377    char dir[256];
00378    char filename[256];
00379    char path[256];
00380    char str[256];
00381    INT status, size, fh, semaphore;
00382    HNDLE hDB, hKey;
00383 
00384    if (rpc_is_remote())
00385       return rpc_call(RPC_CM_MSG_LOG, message_type, message);
00386 
00387    if (message_type != MT_DEBUG) {
00388       cm_get_experiment_database(&hDB, NULL);
00389 
00390       if (hDB) {
00391 
00392          strcpy(filename, "midas.log");
00393          size = sizeof(filename);
00394          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00395 
00396          if (strchr(filename, '%')) {
00397             /* replace stings such as midas_%y%m%d.mid with current date */
00398             time_t now;
00399             struct tm *tms;
00400 
00401             tzset();
00402             time(&now);
00403             tms = localtime(&now);
00404 
00405             strftime(str, sizeof(str), filename, tms);
00406             strlcpy(filename, str, sizeof(filename));
00407          }
00408 
00409          if (strchr(filename, DIR_SEPARATOR) == NULL) {
00410             status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00411             if (status == DB_SUCCESS) {
00412                size = sizeof(dir);
00413                memset(dir, 0, size);
00414                db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00415                if (dir[0] != 0)
00416                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00417                      strcat(dir, DIR_SEPARATOR_STR);
00418 
00419                strcpy(path, dir);
00420                strcat(path, filename);
00421             } else {
00422                cm_get_path(dir);
00423                if (dir[0] != 0)
00424                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00425                      strcat(dir, DIR_SEPARATOR_STR);
00426 
00427                strcpy(path, dir);
00428                strcat(path, "midas.log");
00429             }
00430          } else {
00431             strcpy(path, filename);
00432          }
00433       } else
00434          strcpy(path, "midas.log");
00435 
00436       fh = open(path, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
00437       if (fh < 0) {
00438          printf("Cannot open message log file %s\n", path);
00439       } else {
00440 
00441          cm_get_experiment_semaphore(NULL, NULL, NULL, &semaphore);
00442          status = ss_semaphore_wait_for(semaphore, 5 * 1000);
00443 
00444          strcpy(str, ss_asctime());
00445          write(fh, str, strlen(str));
00446          write(fh, " ", 1);
00447          write(fh, message, strlen(message));
00448          write(fh, "\n", 1);
00449          close(fh);
00450 
00451          ss_semaphore_release(semaphore);
00452       }
00453    }
00454 
00455    return CM_SUCCESS;
00456 }
00457 
00458 /********************************************************************/
00459 /**
00460 Write message to logging file. Called by cm_msg().
00461 @internal
00462 @param message_type      Message type
00463 @param message          Message string
00464 @param facility         Message facility, filename in which messages will be written
00465 @return CM_SUCCESS
00466 */
00467 INT cm_msg_log1(INT message_type, const char *message, const char *facility)
00468 /********************************************************************\
00469 
00470   Routine: cm_msg_log1
00471 
00472   Purpose: Write message to logging file. Called by cm_msg.
00473            Internal use only
00474 
00475   Input:
00476     INT    message_type      Message type
00477     char   *message          Message string
00478     char   *
00479 
00480   Output:
00481     none
00482 
00483   Function value:
00484     CM_SUCCESS
00485 
00486 \********************************************************************/
00487 {
00488    char dir[256];
00489    char filename[256];
00490    char path[256];
00491    char str[256];
00492    FILE *f;
00493    INT status, size;
00494    HNDLE hDB, hKey;
00495 
00496 
00497    if (rpc_is_remote())
00498       return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
00499 
00500    if (message_type != MT_DEBUG) {
00501       cm_get_experiment_database(&hDB, NULL);
00502 
00503       if (hDB) {
00504          strcpy(filename, "midas.log");
00505          size = sizeof(filename);
00506          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00507 
00508          if (strchr(filename, '%')) {
00509             /* replace stings such as midas_%y%m%d.mid with current date */
00510             time_t now;
00511             struct tm *tms;
00512 
00513             tzset();
00514             time(&now);
00515             tms = localtime(&now);
00516 
00517             strftime(str, sizeof(str), filename, tms);
00518             strlcpy(filename, str, sizeof(filename));
00519          }
00520 
00521          if (strchr(filename, DIR_SEPARATOR) == NULL) {
00522 
00523             status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00524             if (status == DB_SUCCESS) {
00525                size = sizeof(dir);
00526                memset(dir, 0, size);
00527                db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00528                if (dir[0] != 0)
00529                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00530                      strcat(dir, DIR_SEPARATOR_STR);
00531 
00532                if (facility[0]) {
00533                   strcpy(filename, facility);
00534                   strcat(filename, ".log");
00535                } else {
00536                   strcpy(filename, "midas.log");
00537                   size = sizeof(filename);
00538                   db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00539 
00540                   if (strchr(filename, '%')) {
00541                      /* replace stings such as midas_%y%m%d.mid with current date */
00542                      time_t now;
00543                      struct tm *tms;
00544 
00545                      tzset();
00546                      time(&now);
00547                      tms = localtime(&now);
00548 
00549                      strftime(str, sizeof(str), filename, tms);
00550                      strlcpy(filename, str, sizeof(filename));
00551                   }
00552                }
00553 
00554                strcpy(path, dir);
00555                strcat(path, filename);
00556             } else {
00557                cm_get_path(dir);
00558                if (dir[0] != 0)
00559                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00560                      strcat(dir, DIR_SEPARATOR_STR);
00561 
00562                strcpy(path, dir);
00563                if (facility[0]) {
00564                   strcat(path, facility);
00565                   strcat(path, ".log");
00566                } else
00567                   strcat(path, "midas.log");
00568             }
00569          } else {
00570             strcpy(path, filename);
00571             *(strrchr(path, DIR_SEPARATOR) + 1) = 0;
00572             if (facility[0]) {
00573                strcat(path, facility);
00574                strcat(path, ".log");
00575             } else
00576                strcat(path, "midas.log");
00577          }
00578       } else {
00579          if (facility[0]) {
00580             strcpy(path, facility);
00581             strcat(path, ".log");
00582          } else
00583             strcpy(path, "midas.log");
00584       }
00585 
00586       f = fopen(path, "a");
00587       if (f == NULL) {
00588          printf("Cannot open message log file %s\n", path);
00589       } else {
00590          strcpy(str, ss_asctime());
00591          fprintf(f, "%s", str);
00592          fprintf(f, " %s\n", message);
00593          fclose(f);
00594       }
00595    }
00596 
00597    return CM_SUCCESS;
00598 }
00599 
00600 /********************************************************************/
00601 /**
00602 This routine can be called whenever an internal error occurs
00603 or an informative message is produced. Different message
00604 types can be enabled or disabled by setting the type bits
00605 via cm_set_msg_print().
00606 @attention Do not add the "\n" escape carriage control at the end of the
00607 formated line as it is already added by the client on the receiving side.
00608 \code
00609    ...
00610    cm_msg(MINFO, "my program", "This is a information message only);
00611    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
00612    cm_msg(MTALK, "my_program", My program is Done!");
00613    ...
00614 \endcode
00615 @param message_type (See @ref midas_macro).
00616 @param filename Name of source file where error occured
00617 @param line Line number where error occured
00618 @param routine Routine name.
00619 @param format message to printout, ... Parameters like for printf()
00620 @return CM_SUCCESS
00621 */
00622 INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
00623 {
00624    va_list argptr;
00625    char event[1000], type_str[256], str[1000], format_cpy[900], local_message[1000], send_message[1000];
00626    const char *pc;
00627    EVENT_HEADER *pevent;
00628    INT status;
00629    static BOOL in_routine = FALSE;
00630 
00631    /* avoid recursive calls */
00632    if (in_routine)
00633       return 0;
00634 
00635    in_routine = TRUE;
00636 
00637    /* strip path */
00638    pc = filename + strlen(filename);
00639    while (*pc != '\\' && *pc != '/' && pc != filename)
00640       pc--;
00641    if (pc != filename)
00642       pc++;
00643 
00644    /* convert type to string */
00645    type_str[0] = 0;
00646    if (message_type & MT_ERROR)
00647       strlcat(type_str, MT_ERROR_STR, sizeof(type_str));
00648    if (message_type & MT_INFO)
00649       strlcat(type_str, MT_INFO_STR, sizeof(type_str));
00650    if (message_type & MT_DEBUG)
00651       strlcat(type_str, MT_DEBUG_STR, sizeof(type_str));
00652    if (message_type & MT_USER)
00653       strlcat(type_str, MT_USER_STR, sizeof(type_str));
00654    if (message_type & MT_LOG)
00655       strlcat(type_str, MT_LOG_STR, sizeof(type_str));
00656    if (message_type & MT_TALK)
00657       strlcat(type_str, MT_TALK_STR, sizeof(type_str));
00658 
00659    /* print client name into string */
00660    if (message_type == MT_USER)
00661       sprintf(send_message, "[%s] ", routine);
00662    else {
00663       rpc_get_name(str);
00664       if (str[0])
00665          sprintf(send_message, "[%s,%s] ", str, type_str);
00666       else
00667          send_message[0] = 0;
00668    }
00669 
00670    local_message[0] = 0;
00671 
00672    /* preceed error messages with file and line info */
00673    if (message_type == MT_ERROR) {
00674       sprintf(str, "[%s:%d:%s,%s] ", pc, line, routine, type_str);
00675       strlcat(send_message, str, sizeof(send_message));
00676       strlcat(local_message, str, sizeof(send_message));
00677    } else if (message_type == MT_USER)
00678       sprintf(local_message, "[%s,%s] ", routine, type_str);
00679 
00680    /* limit length of format */
00681    strlcpy(format_cpy, format, sizeof(format_cpy));
00682 
00683    /* print argument list into message */
00684    va_start(argptr, format);
00685    vsprintf(str, (char *) format, argptr);
00686    va_end(argptr);
00687    strcat(send_message, str);
00688    strcat(local_message, str);
00689 
00690    /* call user function if set via cm_set_msg_print */
00691    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00692       _message_print(local_message);
00693 
00694    /* return if system mask is not set */
00695    if ((message_type & _message_mask_system) == 0) {
00696       in_routine = FALSE;
00697       return CM_SUCCESS;
00698    }
00699 
00700    /* copy message to event */
00701    pevent = (EVENT_HEADER *) event;
00702    strlcpy(event + sizeof(EVENT_HEADER), send_message, sizeof(event) - sizeof(EVENT_HEADER));
00703 
00704    /* send event if not of type MLOG */
00705    if (message_type != MT_LOG) {
00706       /* if no message buffer already opened, do so now */
00707       if (_msg_buffer == 0) {
00708          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00709          if (status != BM_SUCCESS && status != BM_CREATED) {
00710             in_routine = FALSE;
00711             return status;
00712          }
00713       }
00714 
00715       /* setup the event header and send the message */
00716       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, strlen(event + sizeof(EVENT_HEADER)) + 1,
00717                        0);
00718       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00719    }
00720 
00721    /* log message */
00722    cm_msg_log(message_type, send_message);
00723 
00724    in_routine = FALSE;
00725 
00726    return CM_SUCCESS;
00727 }
00728 
00729 /********************************************************************/
00730 /**
00731 This routine is similar to @ref cm_msg().
00732 It differs from cm_msg() only by the logging destination being a file
00733 given through the argument list i.e:\b facility
00734 @internal
00735 @attention Do not add the "\n" escape carriage control at the end of the
00736 formated line as it is already added by the client on the receiving side.
00737 The first arg in the following example uses the predefined
00738 macro MINFO which handles automatically the first 3 arguments of the function
00739 (see @ref midas_macro).
00740 \code   ...
00741    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
00742    ...
00743 //----- File my_log_file.log
00744 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
00745 \endcode
00746 @param message_type See @ref midas_macro.
00747 @param filename Name of source file where error occured
00748 @param line Line number where error occured
00749 @param facility Logging file name
00750 @param routine Routine name
00751 @param format message to printout, ... Parameters like for printf()
00752 @return CM_SUCCESS
00753 */
00754 INT cm_msg1(INT message_type, const char *filename, INT line,
00755             const char *facility, const char *routine, const char *format, ...)
00756 {
00757    va_list argptr;
00758    char event[1000], str[256], local_message[256], send_message[256];
00759    const char *pc;
00760    EVENT_HEADER *pevent;
00761    INT status;
00762    static BOOL in_routine = FALSE;
00763 
00764    /* avoid recursive calles */
00765    if (in_routine)
00766       return 0;
00767 
00768    in_routine = TRUE;
00769 
00770    /* strip path */
00771    pc = filename + strlen(filename);
00772    while (*pc != '\\' && *pc != '/' && pc != filename)
00773       pc--;
00774    if (pc != filename)
00775       pc++;
00776 
00777    /* print client name into string */
00778    if (message_type == MT_USER)
00779       sprintf(send_message, "[%s] ", routine);
00780    else {
00781       rpc_get_name(str);
00782       if (str[0])
00783          sprintf(send_message, "[%s] ", str);
00784       else
00785          send_message[0] = 0;
00786    }
00787 
00788    local_message[0] = 0;
00789 
00790    /* preceed error messages with file and line info */
00791    if (message_type == MT_ERROR) {
00792       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
00793       strcat(send_message, str);
00794       strcat(local_message, str);
00795    }
00796 
00797    /* print argument list into message */
00798    va_start(argptr, format);
00799    vsprintf(str, (char *) format, argptr);
00800    va_end(argptr);
00801 
00802    if (facility)
00803       sprintf(local_message + strlen(local_message), "{%s} ", facility);
00804 
00805    strcat(send_message, str);
00806    strcat(local_message, str);
00807 
00808    /* call user function if set via cm_set_msg_print */
00809    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00810       _message_print(local_message);
00811 
00812    /* return if system mask is not set */
00813    if ((message_type & _message_mask_system) == 0) {
00814       in_routine = FALSE;
00815       return CM_SUCCESS;
00816    }
00817 
00818    /* copy message to event */
00819    pevent = (EVENT_HEADER *) event;
00820    strcpy(event + sizeof(EVENT_HEADER), send_message);
00821 
00822    /* send event if not of type MLOG */
00823    if (message_type != MT_LOG) {
00824       /* if no message buffer already opened, do so now */
00825       if (_msg_buffer == 0) {
00826          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00827          if (status != BM_SUCCESS && status != BM_CREATED) {
00828             in_routine = FALSE;
00829             return status;
00830          }
00831       }
00832 
00833       /* setup the event header and send the message */
00834       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, strlen(event + sizeof(EVENT_HEADER)) + 1,
00835                        0);
00836       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00837    }
00838 
00839    /* log message */
00840    cm_msg_log1(message_type, send_message, facility);
00841 
00842    in_routine = FALSE;
00843 
00844    return CM_SUCCESS;
00845 }
00846 
00847 /********************************************************************/
00848 /**
00849 Register a dispatch function for receiving system messages.
00850 - example code from mlxspeaker.c
00851 \code
00852 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
00853 {
00854   char str[256], *pc, *sp;
00855   // print message
00856   printf("%s\n", (char *)(message));
00857 
00858   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
00859                  ,header->event_id
00860                  ,header->trigger_mask
00861                  ,header->serial_number
00862                  ,header->data_size);
00863   pc = strchr((char *)(message),']')+2;
00864   ...
00865   // skip none talking message
00866   if (header->trigger_mask == MT_TALK ||
00867       header->trigger_mask == MT_USER)
00868    ...
00869 }
00870 
00871 int main(int argc, char *argv[])
00872 {
00873   ...
00874   // now connect to server
00875   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
00876   if (status != CM_SUCCESS)
00877     return 1;
00878   // Register callback for messages
00879   cm_msg_register(receive_message);
00880   ...
00881 }
00882 \endcode
00883 @param func Dispatch function.
00884 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
00885 */
00886 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
00887 {
00888    INT status, id;
00889 
00890    /* if no message buffer already opened, do so now */
00891    if (_msg_buffer == 0) {
00892       status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00893       if (status != BM_SUCCESS && status != BM_CREATED)
00894          return status;
00895    }
00896 
00897    _msg_dispatch = func;
00898 
00899    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_NONBLOCKING, &id, func);
00900 
00901    return status;
00902 }
00903 
00904 /* Retrieve message from an individual file. Internal use only */
00905 INT cm_msg_retrieve1(char *filename, INT n_message, char *message, INT buf_size)
00906 {
00907    FILE *f;
00908    INT offset, i, j;
00909    char *p;
00910 
00911    f = fopen(filename, "rb");
00912    if (f == NULL) {
00913       sprintf(message, "Cannot open message log file %s\n", filename);
00914       return -1;
00915    }
00916 
00917    /* position buf_size bytes before the EOF */
00918    fseek(f, -(buf_size - 1), SEEK_END);
00919    offset = ftell(f);
00920    if (offset != 0) {
00921       /* go to end of line */
00922       fgets(message, buf_size - 1, f);
00923       offset = ftell(f) - offset;
00924       buf_size -= offset;
00925    }
00926 
00927    memset(message, 0, buf_size);
00928    fread(message, 1, buf_size - 1, f);
00929    message[buf_size - 1] = 0;
00930    fclose(f);
00931 
00932    p = message + (buf_size - 2);
00933 
00934    /* goto end of buffer */
00935    while (p != message && *p == 0)
00936       p--;
00937 
00938    /* strip line break */
00939    j = 0;
00940    while (p != message && (*p == '\n' || *p == '\r')) {
00941       *(p--) = 0;
00942       j = 1;
00943    }
00944 
00945 
00946    /* trim buffer so that last n_messages remain */
00947    for (i = 0; i < n_message; i++) {
00948       while (p != message && *p != '\n')
00949          p--;
00950 
00951       while (p != message && (*p == '\n' || *p == '\r'))
00952          p--;
00953 
00954       if (p == message)
00955          break;
00956 
00957       j++;
00958    }
00959    if (p != message) {
00960       p++;
00961       while (*p == '\n' || *p == '\r')
00962          p++;
00963    }
00964 
00965    buf_size = (buf_size - 1) - (p - message);
00966 
00967    memmove(message, p, buf_size);
00968    message[buf_size] = 0;
00969 
00970    return j;
00971 }
00972 
00973 /********************************************************************/
00974 /**
00975 Retrieve old messages from log file
00976 @param  n_message        Number of messages to retrieve
00977 @param  message          buf_size bytes of messages, separated
00978                          by \n characters. The returned number
00979                          of bytes is normally smaller than the
00980                          initial buf_size, since only full
00981                          lines are returned.
00982 @param *buf_size         Size of message buffer to fill
00983 @return CM_SUCCESS
00984 */
00985 INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
00986 {
00987    char dir[256], str[256];
00988    char filename[256], filename2[256];
00989    char path[256], *message2;
00990    INT status, size, n, i;
00991    HNDLE hDB, hKey;
00992    time_t now;
00993    struct tm *tms;
00994 
00995    if (rpc_is_remote())
00996       return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
00997 
00998    cm_get_experiment_database(&hDB, NULL);
00999    status = 0;
01000 
01001    if (hDB) {
01002       strcpy(filename, "midas.log");
01003       size = sizeof(filename);
01004       db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
01005 
01006       strlcpy(filename2, filename, sizeof(filename2));
01007 
01008       if (strchr(filename, '%')) {
01009          /* replace strings such as midas_%y%m%d.mid with current date */
01010          tzset();
01011          time(&now);
01012          tms = localtime(&now);
01013 
01014          strftime(filename2, sizeof(str), filename, tms);
01015       }
01016 
01017       if (strchr(filename2, DIR_SEPARATOR) == NULL) {
01018          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01019          if (status == DB_SUCCESS) {
01020             size = sizeof(dir);
01021             memset(dir, 0, size);
01022             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01023             if (dir[0] != 0)
01024                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01025                   strcat(dir, DIR_SEPARATOR_STR);
01026 
01027             strcpy(path, dir);
01028             strcat(path, filename2);
01029          } else {
01030             cm_get_path(dir);
01031             if (dir[0] != 0)
01032                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01033                   strcat(dir, DIR_SEPARATOR_STR);
01034 
01035             strcpy(path, dir);
01036             strcat(path, filename2);
01037          }
01038       } else {
01039          strcpy(path, filename2);
01040       }
01041    } else
01042       strcpy(path, "midas.log");
01043 
01044    n = cm_msg_retrieve1(path, n_message, message, buf_size);
01045 
01046    while (n < n_message && strchr(filename, '%')) {
01047       now -= 3600 * 24;         // go one day back 
01048       tms = localtime(&now);
01049 
01050       strftime(filename2, sizeof(str), filename, tms);
01051 
01052       if (strchr(filename2, DIR_SEPARATOR) == NULL) {
01053          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01054          if (status == DB_SUCCESS) {
01055             size = sizeof(dir);
01056             memset(dir, 0, size);
01057             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01058             if (dir[0] != 0)
01059                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01060                   strcat(dir, DIR_SEPARATOR_STR);
01061 
01062             strcpy(path, dir);
01063             strcat(path, filename2);
01064          } else {
01065             cm_get_path(dir);
01066             if (dir[0] != 0)
01067                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01068                   strcat(dir, DIR_SEPARATOR_STR);
01069 
01070             strcpy(path, dir);
01071             strcat(path, filename2);
01072          }
01073       } else {
01074          strcpy(path, filename2);
01075       }
01076 
01077       message2 = (char *) malloc(buf_size);
01078 
01079       i = cm_msg_retrieve1(path, n_message - n, message2, buf_size - strlen(message) - 1);
01080       if (i < 0)
01081          break;
01082       strlcat(message2, "\r\n", buf_size);
01083 
01084       memmove(message + strlen(message2), message, strlen(message) + 1);
01085       memcpy(message, message2, strlen(message2));
01086       free(message2);
01087       n += i;
01088    }
01089 
01090    return status;
01091 }
01092 
01093 /**dox***************************************************************/
01094                                                                                                                                /** @} *//* end of msgfunctionc */
01095 
01096 /**dox***************************************************************/
01097 /** @addtogroup cmfunctionc
01098  *
01099  *  @{  */
01100 
01101 /********************************************************************/
01102 /**
01103 Get time from MIDAS server and set local time.
01104 @param    seconds         Time in seconds
01105 @return CM_SUCCESS
01106 */
01107 INT cm_synchronize(DWORD * seconds)
01108 {
01109    INT sec, status;
01110 
01111    /* if connected to server, get time from there */
01112    if (rpc_is_remote()) {
01113       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
01114 
01115       /* set local time */
01116       if (status == CM_SUCCESS)
01117          ss_settime(sec);
01118    }
01119 
01120    /* return time to caller */
01121    if (seconds != NULL) {
01122       *seconds = ss_time();
01123    }
01124 
01125    return CM_SUCCESS;
01126 }
01127 
01128 /********************************************************************/
01129 /**
01130 Get time from MIDAS server and set local time.
01131 @param    str            return time string
01132 @param    buf_size       Maximum size of str
01133 @return   CM_SUCCESS
01134 */
01135 INT cm_asctime(char *str, INT buf_size)
01136 {
01137    /* if connected to server, get time from there */
01138    if (rpc_is_remote())
01139       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
01140 
01141    /* return local time */
01142    strcpy(str, ss_asctime());
01143 
01144    return CM_SUCCESS;
01145 }
01146 
01147 /********************************************************************/
01148 /**
01149 Get time from ss_time on server.
01150 @param    t string
01151 @return   CM_SUCCESS
01152 */
01153 INT cm_time(DWORD * t)
01154 {
01155    /* if connected to server, get time from there */
01156    if (rpc_is_remote())
01157       return rpc_call(RPC_CM_TIME, t);
01158 
01159    /* return local time */
01160    *t = ss_time();
01161 
01162    return CM_SUCCESS;
01163 }
01164 
01165 /**dox***************************************************************/
01166                                                                                                                                /** @} *//* end of cmfunctionc */
01167 
01168 /********************************************************************\
01169 *                                                                    *
01170 *           cm_xxx  -  Common Functions to buffer & database         *
01171 *                                                                    *
01172 \********************************************************************/
01173 
01174 /* Globals */
01175 
01176 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
01177 static HNDLE _hDB = 0;          /* Database handle */
01178 static char _client_name[NAME_LENGTH];
01179 static char _path_name[MAX_STRING_LENGTH];
01180 static INT _call_watchdog = TRUE;
01181 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01182 INT _semaphore_alarm, _semaphore_elog, _semaphore_history, _semaphore_msg;
01183 
01184 /**dox***************************************************************/
01185 /** @addtogroup cmfunctionc
01186  *
01187  *  @{  */
01188 
01189 /**
01190 Return version number of current MIDAS library as a string
01191 @return version number
01192 */
01193 char *cm_get_version()
01194 {
01195    return MIDAS_VERSION;
01196 }
01197 
01198 /**
01199 Return svn revision number of current MIDAS library as a string
01200 @return revision number
01201 */
01202 int cm_get_revision()
01203 {
01204    return atoi(svn_revision + 6);
01205 }
01206 
01207 /********************************************************************/
01208 /**
01209 Set path to actual experiment. This function gets called
01210 by cm_connect_experiment if the connection is established
01211 to a local experiment (not through the TCP/IP server).
01212 The path is then used for all shared memory routines.
01213 @param  path             Pathname
01214 @return CM_SUCCESS
01215 */
01216 INT cm_set_path(char *path)
01217 {
01218    strcpy(_path_name, path);
01219 
01220    /* check for trailing directory seperator */
01221    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01222       strcat(_path_name, DIR_SEPARATOR_STR);
01223 
01224    return CM_SUCCESS;
01225 }
01226 
01227 /********************************************************************/
01228 /**
01229 Return the path name previously set with cm_set_path.
01230 @param  path             Pathname
01231 @return CM_SUCCESS
01232 */
01233 INT cm_get_path(char *path)
01234 {
01235    strcpy(path, _path_name);
01236 
01237    return CM_SUCCESS;
01238 }
01239 
01240 /**dox***************************************************************/
01241                                                                                                                                /** @} *//* end of cmfunctionc */
01242 
01243 /**dox***************************************************************/
01244 /** @addtogroup cmfunctionc
01245  *
01246  *  @{  */
01247 
01248 /**dox***************************************************************/
01249 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01250 
01251 typedef struct {
01252    char name[NAME_LENGTH];
01253    char directory[MAX_STRING_LENGTH];
01254    char user[NAME_LENGTH];
01255 } experiment_table;
01256 
01257 static experiment_table exptab[MAX_EXPERIMENT];
01258 
01259 /**dox***************************************************************/
01260 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01261 
01262 /**
01263 Scan the "exptab" file for MIDAS experiment names and save them
01264 for later use by rpc_server_accept(). The file is first searched
01265 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01266 @return CM_SUCCESS<br>
01267         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01268 */
01269 INT cm_scan_experiments(void)
01270 {
01271    INT i;
01272    FILE *f;
01273    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01274 
01275    for (i = 0; i < MAX_EXPERIMENT; i++)
01276       exptab[i].name[0] = 0;
01277 
01278    /* MIDAS_DIR overrides exptab */
01279    if (getenv("MIDAS_DIR")) {
01280       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01281 
01282       strcpy(exptab[0].name, "Default");
01283       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01284       exptab[0].user[0] = 0;
01285 
01286       return CM_SUCCESS;
01287    }
01288 
01289    /* default directory for different OSes */
01290 #if defined (OS_WINNT)
01291    if (getenv("SystemRoot"))
01292       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01293    else if (getenv("windir"))
01294       strlcpy(str, getenv("windir"), sizeof(str));
01295    else
01296       strcpy(str, "");
01297 
01298    strcpy(alt_str, str);
01299    strcat(str, "\\system32\\exptab");
01300    strcat(alt_str, "\\system\\exptab");
01301 #elif defined (OS_UNIX)
01302    strcpy(str, "/etc/exptab");
01303    strcpy(alt_str, "/exptab");
01304 #else
01305    strcpy(str, "exptab");
01306    strcpy(alt_str, "exptab");
01307 #endif
01308 
01309    /* MIDAS_EXPTAB overrides default directory */
01310    if (getenv("MIDAS_EXPTAB")) {
01311       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01312       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01313    }
01314 
01315    /* read list of available experiments */
01316    f = fopen(str, "r");
01317    if (f == NULL) {
01318       f = fopen(alt_str, "r");
01319       if (f == NULL)
01320          return CM_UNDEF_ENVIRON;
01321    }
01322 
01323    i = 0;
01324    if (f != NULL) {
01325       do {
01326          str[0] = 0;
01327          if (fgets(str, 100, f) == NULL)
01328             break;
01329          if (str[0] && str[0] != '#' && str[0] != ' ' && str[0] != '\t'
01330              && (strchr(str, ' ') || strchr(str, '\t'))) {
01331             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01332 
01333             /* check for trailing directory separator */
01334             pdir = exptab[i].directory;
01335             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01336                strcat(pdir, DIR_SEPARATOR_STR);
01337 
01338             i++;
01339          }
01340       } while (!feof(f));
01341       fclose(f);
01342    }
01343 
01344    /*
01345       for (j=0 ; j<i ; j++)
01346       {
01347       sprintf(str, "Scanned experiment %s", exptab[j].name);
01348       cm_msg(MINFO, str);
01349       }
01350     */
01351 
01352    return CM_SUCCESS;
01353 }
01354 
01355 /********************************************************************/
01356 /**
01357 Delete client info from database
01358 @param hDB               Database handle
01359 @param pid               PID of entry to delete, zero for this process.
01360 @return CM_SUCCESS
01361 */
01362 INT cm_delete_client_info(HNDLE hDB, INT pid)
01363 {
01364 #ifdef LOCAL_ROUTINES
01365 
01366    /* only do it if local */
01367    if (!rpc_is_remote()) {
01368       INT status;
01369       HNDLE hKey;
01370       char str[256];
01371 
01372       if (!pid)
01373          pid = ss_getpid();
01374 
01375       /* don't delete info from a closed database */
01376       if (_database_entries == 0)
01377          return CM_SUCCESS;
01378 
01379       /* make operation atomic by locking database */
01380       db_lock_database(hDB);
01381 
01382       sprintf(str, "System/Clients/%0d", pid);
01383       status = db_find_key1(hDB, 0, str, &hKey);
01384       if (status != DB_SUCCESS) {
01385          db_unlock_database(hDB);
01386          return status;
01387       }
01388 
01389       /* unlock client entry and delete it without locking DB */
01390       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01391       db_delete_key1(hDB, hKey, 1, TRUE);
01392 
01393       db_unlock_database(hDB);
01394 
01395       /* touch notify key to inform others */
01396       status = 0;
01397       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01398    }
01399 #endif                          /*LOCAL_ROUTINES */
01400 
01401    return CM_SUCCESS;
01402 }
01403 
01404 /********************************************************************/
01405 /**
01406 Check if a client with a /system/client/xxx entry has
01407 a valid entry in the ODB client table. If not, remove
01408 that client from the /system/client tree.
01409 @param   hDB               Handle to online database
01410 @param   hKeyClient        Handle to client key
01411 @return  CM_SUCCESS, CM_NO_CLIENT
01412 */
01413 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01414 {
01415    if (rpc_is_remote())
01416       return rpc_call(RPC_CM_CHECK_CLIENT, hDB, hKeyClient);
01417 
01418 #ifdef LOCAL_ROUTINES
01419    {
01420       KEY key;
01421       DATABASE_HEADER *pheader;
01422       DATABASE_CLIENT *pclient;
01423       INT i, client_pid, status, dead = 0, found = 0;
01424       char name[NAME_LENGTH];
01425 
01426       db_get_key(hDB, hKeyClient, &key);
01427       client_pid = atoi(key.name);
01428 
01429       i = sizeof(name);
01430       db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01431 
01432       db_lock_database(hDB);
01433       if (_database[hDB - 1].attached) {
01434          pheader = _database[hDB - 1].database_header;
01435          pclient = pheader->client;
01436 
01437          /* loop through clients */
01438          for (i = 0; i < pheader->max_client_index; i++, pclient++)
01439             if (pclient->pid == client_pid) {
01440                found = 1;
01441                break;
01442             }
01443 #ifdef OS_UNIX
01444 #ifdef ESRCH
01445          if (found) {           /* check that the client is still running: PID still exists */
01446             /* Only enable this for systems that define ESRCH and hope that they also support kill(pid,0) */
01447             errno = 0;
01448             kill(client_pid, 0);
01449             if (errno == ESRCH) {
01450                cm_msg(MERROR, "cm_check_client",
01451                       "Removing client \'%s\', pid %d, index %d because this pid no longer exists",
01452                       pclient->name, client_pid, i);
01453                dead = 1;
01454             }
01455          }
01456 #endif
01457 #endif
01458 
01459          if (!found || dead) {
01460             /* client not found : delete ODB stucture */
01461             db_unlock_database(hDB);
01462 
01463             status = cm_delete_client_info(hDB, client_pid);
01464             if (status != CM_SUCCESS)
01465                cm_msg(MERROR, "cm_check_client",
01466                       "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
01467                       status);
01468             else
01469                cm_msg(MINFO, "cm_check_client",
01470                       "Deleted entry \'/System/Clients/%d\' for client \'%s\'", client_pid, name);
01471 
01472             return CM_NO_CLIENT;
01473          }
01474       }
01475 
01476       db_unlock_database(hDB);
01477    }
01478 #endif                          /*LOCAL_ROUTINES */
01479 
01480    return CM_SUCCESS;
01481 }
01482 
01483 /********************************************************************/
01484 /**
01485 Set client information in online database and return handle
01486 @param  hDB              Handle to online database
01487 @param  hKeyClient       returned key
01488 @param  host_name        server name
01489 @param  client_name      Name of this program as it will be seen
01490                          by other clients.
01491 @param  hw_type          Type of byte order
01492 @param  password         MIDAS password
01493 @param  watchdog_timeout Default watchdog timeout, can be overwritten
01494                          by ODB setting /programs/<name>/Watchdog timeout
01495 @return   CM_SUCCESS
01496 */
01497 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
01498                        char *client_name, INT hw_type, char *password, DWORD watchdog_timeout)
01499 {
01500    if (rpc_is_remote())
01501       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
01502                       host_name, client_name, hw_type, password, watchdog_timeout);
01503 
01504 #ifdef LOCAL_ROUTINES
01505    {
01506       INT status, pid, data, i, idx, size;
01507       HNDLE hKey, hSubkey;
01508       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
01509       BOOL call_watchdog, allow;
01510       PROGRAM_INFO_STR(program_info_str);
01511 
01512       /* check security if password is present */
01513       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
01514       if (hKey) {
01515          /* get password */
01516          size = sizeof(pwd);
01517          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
01518 
01519          /* first check allowed hosts list */
01520          allow = FALSE;
01521          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
01522          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
01523             allow = TRUE;
01524 
01525          /* check allowed programs list */
01526          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
01527          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
01528             allow = TRUE;
01529 
01530          /* now check password */
01531          if (!allow && strcmp(password, pwd) != 0) {
01532             if (password[0])
01533                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
01534             db_close_all_databases();
01535             bm_close_all_buffers();
01536             _msg_buffer = 0;
01537             return CM_WRONG_PASSWORD;
01538          }
01539       }
01540 
01541       /* make following operation atomic by locking database */
01542       db_lock_database(hDB);
01543 
01544       /* check if entry with this pid exists already */
01545       pid = ss_getpid();
01546 
01547       sprintf(str, "System/Clients/%0d", pid);
01548       status = db_find_key(hDB, 0, str, &hKey);
01549       if (status == DB_SUCCESS) {
01550          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
01551          db_delete_key(hDB, hKey, TRUE);
01552       }
01553 
01554       if (strlen(client_name) >= NAME_LENGTH)
01555          client_name[NAME_LENGTH] = 0;
01556 
01557       strcpy(name, client_name);
01558       strcpy(orig_name, client_name);
01559 
01560       /* check if client name already exists */
01561       status = db_find_key(hDB, 0, "System/Clients", &hKey);
01562 
01563       for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
01564          for (i = 0;; i++) {
01565             status = db_enum_key(hDB, hKey, i, &hSubkey);
01566             if (status == DB_NO_MORE_SUBKEYS)
01567                break;
01568 
01569             if (status == DB_SUCCESS) {
01570                size = sizeof(str);
01571                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
01572             }
01573 
01574             /* check if client is living */
01575             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
01576                continue;
01577 
01578             if (equal_ustring(str, name)) {
01579                sprintf(name, "%s%d", client_name, idx);
01580                break;
01581             }
01582          }
01583       }
01584 
01585       /* set name */
01586       sprintf(str, "System/Clients/%0d/Name", pid);
01587       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
01588       if (status != DB_SUCCESS) {
01589          db_unlock_database(hDB);
01590          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
01591          return status;
01592       }
01593 
01594       /* copy new client name */
01595       strcpy(client_name, name);
01596       db_set_client_name(hDB, client_name);
01597 
01598       /* set also as rpc name */
01599       rpc_set_name(client_name);
01600 
01601       /* use /system/clients/PID as root */
01602       sprintf(str, "System/Clients/%0d", pid);
01603       db_find_key(hDB, 0, str, &hKey);
01604 
01605       /* set host name */
01606       status = db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
01607       if (status != DB_SUCCESS) {
01608          db_unlock_database(hDB);
01609          return status;
01610       }
01611 
01612       /* set computer id */
01613       status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT);
01614       if (status != DB_SUCCESS) {
01615          db_unlock_database(hDB);
01616          return status;
01617       }
01618 
01619       /* set server port */
01620       data = 0;
01621       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
01622       if (status != DB_SUCCESS) {
01623          db_unlock_database(hDB);
01624          return status;
01625       }
01626 
01627       /* lock client entry */
01628       db_set_mode(hDB, hKey, MODE_READ, TRUE);
01629 
01630       /* get (set) default watchdog timeout */
01631       size = sizeof(watchdog_timeout);
01632       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
01633       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01634 
01635       /* define /programs entry */
01636       sprintf(str, "/Programs/%s", orig_name);
01637       db_create_record(hDB, 0, str, strcomb(program_info_str));
01638 
01639       /* save handle for ODB and client */
01640       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
01641       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
01642 
01643       /* save watchdog timeout */
01644       cm_get_watchdog_params(&call_watchdog, NULL);
01645       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01646       if (call_watchdog)
01647          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
01648 
01649       /* end of atomic operations */
01650       db_unlock_database(hDB);
01651 
01652       /* touch notify key to inform others */
01653       data = 0;
01654       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
01655 
01656       *hKeyClient = hKey;
01657    }
01658 #endif                          /* LOCAL_ROUTINES */
01659 
01660    return CM_SUCCESS;
01661 }
01662 
01663 /********************************************************************/
01664 /**
01665 Get info about the current client
01666 @param  *client_name       Client name.
01667 @return   CM_SUCCESS, CM_UNDEF_EXP
01668 */
01669 INT cm_get_client_info(char *client_name)
01670 {
01671    INT status, length;
01672    HNDLE hDB, hKey;
01673 
01674    /* get root key of client */
01675    cm_get_experiment_database(&hDB, &hKey);
01676    if (!hDB) {
01677       client_name[0] = 0;
01678       return CM_UNDEF_EXP;
01679    }
01680 
01681    status = db_find_key(hDB, hKey, "Name", &hKey);
01682    if (status != DB_SUCCESS)
01683       return status;
01684 
01685    length = NAME_LENGTH;
01686    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
01687    if (status != DB_SUCCESS)
01688       return status;
01689 
01690    return CM_SUCCESS;
01691 }
01692 
01693 /********************************************************************/
01694 /**
01695 Returns MIDAS environment variables.
01696 @attention This function can be used to evaluate the standard MIDAS
01697            environment variables before connecting to an experiment
01698            (see @ref Environment_variables).
01699            The usual way is that the host name and experiment name are first derived
01700            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
01701            They can then be superseded by command line parameters with -h and -e flags.
01702 \code
01703 #include <stdio.h>
01704 #include <midas.h>
01705 main(int argc, char *argv[])
01706 {
01707   INT  status, i;
01708   char host_name[256],exp_name[32];
01709 
01710   // get default values from environment
01711   cm_get_environment(host_name, exp_name);
01712 
01713   // parse command line parameters
01714   for (i=1 ; i<argc ; i++)
01715     {
01716     if (argv[i][0] == '-')
01717       {
01718       if (i+1 >= argc || argv[i+1][0] == '-')
01719         goto usage;
01720       if (argv[i][1] == 'e')
01721         strcpy(exp_name, argv[++i]);
01722       else if (argv[i][1] == 'h')
01723         strcpy(host_name, argv[++i]);
01724       else
01725         {
01726 usage:
01727         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01728         return 1;
01729         }
01730       }
01731     }
01732   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01733   if (status != CM_SUCCESS)
01734     return 1;
01735     ...do anyting...
01736   cm_disconnect_experiment();
01737 }
01738 \endcode
01739 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
01740 @param host_name_size     string length
01741 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
01742 @param exp_name_size      string length
01743 @return CM_SUCCESS
01744 */
01745 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
01746 {
01747    host_name[0] = exp_name[0] = 0;
01748 
01749    if (getenv("MIDAS_SERVER_HOST"))
01750       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
01751 
01752    if (getenv("MIDAS_EXPT_NAME"))
01753       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
01754 
01755    return CM_SUCCESS;
01756 }
01757 
01758 
01759 /**dox***************************************************************/
01760 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01761 
01762 /********************************************************************/
01763 void cm_check_connect(void)
01764 {
01765    if (_hKeyClient)
01766       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
01767 }
01768 
01769 /**dox***************************************************************/
01770 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01771 
01772 /********************************************************************/
01773 /**
01774 This function connects to an existing MIDAS experiment.
01775 This must be the first call in a MIDAS application.
01776 It opens three TCP connection to the remote host (one for RPC calls,
01777 one to send events and one for hot-link notifications from the remote host)
01778 and writes client information into the ODB under /System/Clients.
01779 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
01780 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
01781 experiment name (see @ref Environment_variables).
01782 For that purpose, the function cm_get_environment()
01783 should be called prior to cm_connect_experiment(). If command line
01784 parameters -h and -e are used, the evaluation should be done between
01785 cm_get_environment() and cm_connect_experiment(). The function
01786 cm_disconnect_experiment() must be called before a MIDAS application exits.
01787 \code
01788 #include <stdio.h>
01789 #include <midas.h>
01790 main(int argc, char *argv[])
01791 {
01792   INT  status, i;
01793   char host_name[256],exp_name[32];
01794 
01795   // get default values from environment
01796   cm_get_environment(host_name, exp_name);
01797 
01798   // parse command line parameters
01799   for (i=1 ; i<argc ; i++)
01800     {
01801     if (argv[i][0] == '-')
01802       {
01803       if (i+1 >= argc || argv[i+1][0] == '-')
01804         goto usage;
01805       if (argv[i][1] == 'e')
01806         strcpy(exp_name, argv[++i]);
01807       else if (argv[i][1] == 'h')
01808         strcpy(host_name, argv[++i]);
01809       else
01810         {
01811 usage:
01812         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01813         return 1;
01814         }
01815       }
01816     }
01817   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01818   if (status != CM_SUCCESS)
01819     return 1;
01820   ...do operations...
01821   cm_disconnect_experiment();
01822 }
01823 \endcode
01824 @param host_name Specifies host to connect to. Must be a valid IP host name.
01825   The string can be empty ("") if to connect to the local computer.
01826 @param exp_name Specifies the experiment to connect to.
01827   If this string is empty, the number of defined experiments in exptab is checked.
01828   If only one experiment is defined, the function automatically connects to this
01829   one. If more than one experiment is defined, a list is presented and the user
01830   can interactively select one experiment.
01831 @param client_name Client name of the calling program as it can be seen by
01832   others (like the scl command in ODBEdit).
01833 @param func Callback function to read in a password if security has
01834   been enabled. In all command line applications this function is NULL which
01835   invokes an internal ss_gets() function to read in a password.
01836   In windows environments (MS Windows, X Windows) a function can be supplied to
01837   open a dialog box and read in the password. The argument of this function must
01838   be the returned password.
01839 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br>
01840 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
01841 */
01842 INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func) (char *))
01843 {
01844    INT status;
01845    char str[256];
01846 
01847    status =
01848        cm_connect_experiment1(host_name, exp_name, client_name, func, DEFAULT_ODB_SIZE,
01849                               DEFAULT_WATCHDOG_TIMEOUT);
01850    if (status != CM_SUCCESS) {
01851       cm_get_error(status, str);
01852       puts(str);
01853    }
01854 
01855    return status;
01856 }
01857 
01858 /********************************************************************/
01859 /**
01860 Connect to a MIDAS experiment (to the online database) on
01861            a specific host.
01862 @internal
01863 */
01864 INT cm_connect_experiment1(const char *host_name, const char *exp_name,
01865                            const char *client_name, void (*func) (char *), INT odb_size, DWORD watchdog_timeout)
01866 {
01867    INT status, i, semaphore_elog, semaphore_alarm, semaphore_history, semaphore_msg, size;
01868    char local_host_name[HOST_NAME_LENGTH];
01869    char client_name1[NAME_LENGTH];
01870    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
01871    HNDLE hDB, hKeyClient;
01872    BOOL call_watchdog;
01873    RUNINFO_STR(runinfo_str);
01874 
01875    if (_hKeyClient)
01876       cm_disconnect_experiment();
01877 
01878    rpc_set_name(client_name);
01879 
01880    /* check for local host */
01881    if (equal_ustring(host_name, "local"))
01882       host_name = NULL;
01883 
01884 #ifdef OS_WINNT
01885    {
01886       WSADATA WSAData;
01887 
01888       /* Start windows sockets */
01889       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01890          return RPC_NET_ERROR;
01891    }
01892 #endif
01893 
01894    /* search for experiment name in exptab */
01895    if (exp_name == NULL)
01896       exp_name = "";
01897 
01898    strcpy(exp_name1, exp_name);
01899    if (exp_name1[0] == 0) {
01900       status = cm_select_experiment(host_name, exp_name1);
01901       if (status != CM_SUCCESS)
01902          return status;
01903    }
01904 
01905    /* connect to MIDAS server */
01906    if (host_name && host_name[0]) {
01907       status = rpc_server_connect(host_name, exp_name1);
01908       if (status != RPC_SUCCESS)
01909          return status;
01910 
01911       /* register MIDAS library functions */
01912       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
01913       if (status != RPC_SUCCESS)
01914          return status;
01915    } else {
01916       /* lookup path for *SHM files and save it */
01917       status = cm_scan_experiments();
01918       if (status != CM_SUCCESS)
01919          return status;
01920 
01921       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
01922          if (equal_ustring(exp_name1, exptab[i].name))
01923             break;
01924 
01925       /* return if experiment not defined */
01926       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
01927          /* message should be displayed by application
01928             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
01929             cm_msg(MERROR, str);
01930           */
01931          return CM_UNDEF_EXP;
01932       }
01933 
01934       cm_set_path(exptab[i].directory);
01935 
01936       /* create alarm and elog semaphores */
01937       status = ss_semaphore_create("ALARM", &semaphore_alarm);
01938       if (status != SS_CREATED && status != SS_SUCCESS) {
01939          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
01940          return status;
01941       }
01942       status = ss_semaphore_create("ELOG", &semaphore_elog);
01943       if (status != SS_CREATED && status != SS_SUCCESS) {
01944          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
01945          return status;
01946       }
01947       status = ss_semaphore_create("HISTORY", &semaphore_history);
01948       if (status != SS_CREATED && status != SS_SUCCESS) {
01949          cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
01950          return status;
01951       }
01952       status = ss_semaphore_create("MSG", &semaphore_msg);
01953       if (status != SS_CREATED && status != SS_SUCCESS) {
01954          cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
01955          return status;
01956       }
01957 
01958       cm_set_experiment_semaphore(semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg);
01959    }
01960 
01961    /* open ODB */
01962    if (odb_size == 0)
01963       odb_size = DEFAULT_ODB_SIZE;
01964 
01965    status = db_open_database("ODB", odb_size, &hDB, client_name);
01966    if (status != DB_SUCCESS && status != DB_CREATED) {
01967       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01968       return status;
01969    }
01970 
01971    /* now setup client info */
01972    gethostname(local_host_name, sizeof(local_host_name));
01973 
01974    /* check watchdog timeout */
01975    if (watchdog_timeout == 0)
01976       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01977 
01978    strcpy(client_name1, client_name);
01979    password[0] = 0;
01980    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01981                                client_name1, rpc_get_option(0, RPC_OHW_TYPE), password, watchdog_timeout);
01982 
01983    if (status == CM_WRONG_PASSWORD) {
01984       if (func == NULL)
01985          strcpy(str, ss_getpass("Password: "));
01986       else
01987          func(str);
01988 
01989       /* re-open database */
01990       status = db_open_database("ODB", odb_size, &hDB, client_name);
01991       if (status != DB_SUCCESS && status != DB_CREATED) {
01992          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01993          return status;
01994       }
01995 
01996       strcpy(password, ss_crypt(str, "mi"));
01997       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01998                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE), password, watchdog_timeout);
01999       if (status != CM_SUCCESS) {
02000          /* disconnect */
02001          if (rpc_is_remote())
02002             rpc_server_disconnect();
02003 
02004          return status;
02005       }
02006    }
02007 
02008    cm_set_experiment_database(hDB, hKeyClient);
02009 
02010    /* set experiment name in ODB */
02011    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
02012 
02013    /* set data dir in ODB */
02014    cm_get_path(str);
02015    size = sizeof(str);
02016    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
02017 
02018    /* check /runinfo structure */
02019    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), TRUE);
02020    if (status == DB_STRUCT_MISMATCH) {
02021       cm_msg(MERROR, "cm_connect_experiment1", "Aborting on mismatching /Runinfo structure");
02022       cm_disconnect_experiment();
02023       abort();
02024    }
02025 
02026    /* register server to be able to be called by other clients */
02027    status = cm_register_server();
02028    if (status != CM_SUCCESS)
02029       return status;
02030 
02031    /* set watchdog timeout */
02032    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
02033    size = sizeof(watchdog_timeout);
02034    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
02035    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02036    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02037 
02038    /* send startup notification */
02039    if (strchr(local_host_name, '.'))
02040       *strchr(local_host_name, '.') = 0;
02041 
02042    /* startup message is not displayed */
02043    _message_print = NULL;
02044 
02045    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started", client_name, local_host_name);
02046 
02047    /* enable system and user messages to stdout as default */
02048    cm_set_msg_print(MT_ALL, MT_ALL, puts);
02049 
02050    /* call cm_check_connect when exiting */
02051    atexit((void (*)(void)) cm_check_connect);
02052 
02053    /* register ctrl-c handler */
02054    ss_ctrlc_handler(cm_ctrlc_handler);
02055 
02056    return CM_SUCCESS;
02057 }
02058 
02059 /********************************************************************/
02060 /**
02061 Connect to a MIDAS server and return all defined
02062            experiments in *exp_name[MAX_EXPERIMENTS]
02063 @param  host_name         Internet host name.
02064 @param  exp_name          list of experiment names
02065 @return CM_SUCCESS, RPC_NET_ERROR
02066 */
02067 INT cm_list_experiments(const char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
02068 {
02069    INT i, status;
02070    struct sockaddr_in bind_addr;
02071    INT sock;
02072    char str[MAX_EXPERIMENT * NAME_LENGTH];
02073    struct hostent *phe;
02074    int port = MIDAS_TCP_PORT;
02075    char hname[256];
02076    char *s;
02077 
02078    if (host_name == NULL || host_name[0] == 0 || equal_ustring(host_name, "local")) {
02079       status = cm_scan_experiments();
02080       if (status != CM_SUCCESS)
02081          return status;
02082 
02083       for (i = 0; i < MAX_EXPERIMENT; i++)
02084          strcpy(exp_name[i], exptab[i].name);
02085 
02086       return CM_SUCCESS;
02087    }
02088 #ifdef OS_WINNT
02089    {
02090       WSADATA WSAData;
02091 
02092       /* Start windows sockets */
02093       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02094          return RPC_NET_ERROR;
02095    }
02096 #endif
02097 
02098    /* create a new socket for connecting to remote server */
02099    sock = socket(AF_INET, SOCK_STREAM, 0);
02100    if (sock == -1) {
02101       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
02102       return RPC_NET_ERROR;
02103    }
02104 
02105    /* extract port number from host_name */
02106    strlcpy(hname, host_name, sizeof(hname));
02107    s = strchr(hname, ':');
02108    if (s) {
02109       *s = 0;
02110       port = strtoul(s + 1, NULL, 0);
02111    }
02112 
02113    /* connect to remote node */
02114    memset(&bind_addr, 0, sizeof(bind_addr));
02115    bind_addr.sin_family = AF_INET;
02116    bind_addr.sin_addr.s_addr = 0;
02117    bind_addr.sin_port = htons(port);
02118 
02119 #ifdef OS_VXWORKS
02120    {
02121       INT host_addr;
02122 
02123       host_addr = hostGetByName(hname);
02124       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
02125    }
02126 #else
02127    phe = gethostbyname(hname);
02128    if (phe == NULL) {
02129       cm_msg(MERROR, "cm_list_experiments", "cannot resolve host name \'%s\'", hname);
02130       return RPC_NET_ERROR;
02131    }
02132    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
02133 #endif
02134 
02135 #ifdef OS_UNIX
02136    do {
02137       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
02138 
02139       /* don't return if an alarm signal was cought */
02140    } while (status == -1 && errno == EINTR);
02141 #else
02142    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
02143 #endif
02144 
02145    if (status != 0) {
02146 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
02147       return RPC_NET_ERROR;
02148    }
02149 
02150    /* request experiment list */
02151    send(sock, "I", 2, 0);
02152 
02153    for (i = 0; i < MAX_EXPERIMENT; i++) {
02154       exp_name[i][0] = 0;
02155       status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
02156 
02157       if (status < 0)
02158          return RPC_NET_ERROR;
02159 
02160       if (status == 0)
02161          break;
02162 
02163       strcpy(exp_name[i], str);
02164    }
02165 
02166    exp_name[i][0] = 0;
02167    closesocket(sock);
02168 
02169    return CM_SUCCESS;
02170 }
02171 
02172 /********************************************************************/
02173 /**
02174 Connect to a MIDAS server and select an experiment
02175            from the experiments available on this server
02176 @internal
02177 @param  host_name         Internet host name.
02178 @param  exp_name          list of experiment names
02179 @return CM_SUCCESS, RPC_NET_ERROR
02180 */
02181 INT cm_select_experiment(const char *host_name, char *exp_name)
02182 {
02183    INT status, i;
02184    char expts[MAX_EXPERIMENT][NAME_LENGTH];
02185    char str[32];
02186 
02187    /* retrieve list of experiments and make selection */
02188    status = cm_list_experiments(host_name, expts);
02189    if (status != CM_SUCCESS)
02190       return status;
02191 
02192    if (expts[1][0]) {
02193       if (host_name[0])
02194          printf("Available experiments on server %s:\n", host_name);
02195       else
02196          printf("Available experiments on local computer:\n");
02197 
02198       for (i = 0; expts[i][0]; i++)
02199          printf("%d : %s\n", i, expts[i]);
02200       printf("Select number: ");
02201       ss_gets(str, 32);
02202       i = atoi(str);
02203       strcpy(exp_name, expts[i]);
02204    } else
02205       strcpy(exp_name, expts[0]);
02206 
02207    return CM_SUCCESS;
02208 }
02209 
02210 /********************************************************************/
02211 /**
02212 Connect to a MIDAS client of the current experiment
02213 @internal
02214 @param  client_name       Name of client to connect to. This name
02215                             is set by the other client via the
02216                             cm_connect_experiment call.
02217 @param  hConn            Connection handle
02218 @return CM_SUCCESS, CM_NO_CLIENT
02219 */
02220 INT cm_connect_client(char *client_name, HNDLE * hConn)
02221 {
02222    HNDLE hDB, hKeyRoot, hSubkey, hKey;
02223    INT status, i, length, port;
02224    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
02225 
02226    /* find client entry in ODB */
02227    cm_get_experiment_database(&hDB, &hKey);
02228 
02229    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02230    if (status != DB_SUCCESS)
02231       return status;
02232 
02233    i = 0;
02234    do {
02235       /* search for client with specific name */
02236       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02237       if (status == DB_NO_MORE_SUBKEYS)
02238          return CM_NO_CLIENT;
02239 
02240       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02241       if (status != DB_SUCCESS)
02242          return status;
02243 
02244       length = NAME_LENGTH;
02245       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02246       if (status != DB_SUCCESS)
02247          return status;
02248 
02249       if (equal_ustring(name, client_name)) {
02250          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02251          if (status != DB_SUCCESS)
02252             return status;
02253 
02254          length = sizeof(INT);
02255          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02256          if (status != DB_SUCCESS)
02257             return status;
02258 
02259          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02260          if (status != DB_SUCCESS)
02261             return status;
02262 
02263          length = sizeof(host_name);
02264          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02265          if (status != DB_SUCCESS)
02266             return status;
02267 
02268          /* client found -> connect to its server port */
02269          return rpc_client_connect(host_name, port, client_name, hConn);
02270       }
02271 
02272 
02273    } while (TRUE);
02274 }
02275 
02276 /********************************************************************/
02277 /**
02278 Disconnect from a MIDAS client
02279 @param   hConn             Connection handle obtained via
02280                              cm_connect_client()
02281 @param   bShutdown         If TRUE, disconnect from client and
02282                              shut it down (exit the client program)
02283                              by sending a RPC_SHUTDOWN message
02284 @return   see rpc_client_disconnect()
02285 */
02286 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02287 {
02288    return rpc_client_disconnect(hConn, bShutdown);
02289 }
02290 
02291 /********************************************************************/
02292 /**
02293 Disconnect from a MIDAS experiment.
02294 @attention Should be the last call to a MIDAS library function in an
02295 application before it exits. This function removes the client information
02296 from the ODB, disconnects all TCP connections and frees all internal
02297 allocated memory. See cm_connect_experiment() for example.
02298 @return CM_SUCCESS
02299 */
02300 INT cm_disconnect_experiment(void)
02301 {
02302    HNDLE hDB, hKey;
02303    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02304 
02305    /* send shutdown notification */
02306    rpc_get_name(client_name);
02307    gethostname(local_host_name, sizeof(local_host_name));
02308    if (strchr(local_host_name, '.'))
02309       *strchr(local_host_name, '.') = 0;
02310 
02311    /* disconnect message not displayed */
02312    _message_print = NULL;
02313 
02314    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name, local_host_name);
02315 
02316    if (rpc_is_remote()) {
02317       /* close open records */
02318       db_close_all_records();
02319 
02320       rpc_client_disconnect(-1, FALSE);
02321       rpc_server_disconnect();
02322    } else {
02323       rpc_client_disconnect(-1, FALSE);
02324 
02325 #ifdef LOCAL_ROUTINES
02326       ss_alarm(0, cm_watchdog);
02327       _watchdog_last_called = 0;
02328 #endif                          /* LOCAL_ROUTINES */
02329 
02330       /* delete client info */
02331       cm_get_experiment_database(&hDB, &hKey);
02332 
02333       if (hDB)
02334          cm_delete_client_info(hDB, 0);
02335 
02336       bm_close_all_buffers();
02337       db_close_all_databases();
02338    }
02339 
02340    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02341       rpc_server_shutdown();
02342 
02343    /* free RPC list */
02344    rpc_deregister_functions();
02345 
02346    cm_set_experiment_database(0, 0);
02347 
02348    _msg_buffer = 0;
02349 
02350    /* free memory buffers */
02351    if (_event_buffer_size > 0) {
02352       M_FREE(_event_buffer);
02353       _event_buffer_size = 0;
02354    }
02355 
02356    if (_net_recv_buffer_size > 0) {
02357       M_FREE(_net_recv_buffer);
02358       _net_recv_buffer_size = 0;
02359       _net_recv_buffer_size_odb = 0;
02360    }
02361 
02362    if (_net_send_buffer_size > 0) {
02363       M_FREE(_net_send_buffer);
02364       _net_send_buffer_size = 0;
02365    }
02366 
02367    if (_tcp_buffer != NULL) {
02368       M_FREE(_tcp_buffer);
02369       _tcp_buffer = NULL;
02370    }
02371 
02372    return CM_SUCCESS;
02373 }
02374 
02375 /********************************************************************/
02376 /**
02377 Set the handle to the ODB for the currently connected experiment
02378 @param hDB              Database handle
02379 @param hKeyClient       Key handle of client structure
02380 @return CM_SUCCESS
02381 */
02382 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02383 {
02384    _hDB = hDB;
02385    _hKeyClient = hKeyClient;
02386 
02387    return CM_SUCCESS;
02388 }
02389 
02390 
02391 
02392 /**dox***************************************************************/
02393 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02394 
02395 /********************************************************************/
02396 INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
02397 /********************************************************************\
02398 
02399   Routine: cm_set_experiment_semaphore
02400 
02401   Purpose: Set the handle to the experiment wide semaphorees
02402 
02403   Input:
02404     INT    semaphore_alarm      Alarm semaphore
02405     INT    semaphore_elog       Elog semaphore
02406     INT    semaphore_history    History semaphore
02407     INT    semaphore_msg        Message semaphore
02408 
02409   Output:
02410     none
02411 
02412   Function value:
02413     CM_SUCCESS              Successful completion
02414 
02415 \********************************************************************/
02416 {
02417    _semaphore_alarm = semaphore_alarm;
02418    _semaphore_elog = semaphore_elog;
02419    _semaphore_history = semaphore_history;
02420    _semaphore_msg = semaphore_msg;
02421 
02422    return CM_SUCCESS;
02423 }
02424 
02425 /**dox***************************************************************/
02426 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02427 
02428 /********************************************************************/
02429 /**
02430 Get the handle to the ODB from the currently connected experiment.
02431 
02432 @attention This function returns the handle of the online database (ODB) which
02433 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02434 to access the client information in the ODB. If the client key handle is not needed,
02435 the parameter can be NULL.
02436 \code
02437 HNDLE hDB, hkeyclient;
02438  char  name[32];
02439  int   size;
02440  db_get_experiment_database(&hdb, &hkeyclient);
02441  size = sizeof(name);
02442  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02443  printf("My name is %s\n", name);
02444 \endcode
02445 @param hDB Database handle.
02446 @param hKeyClient Handle for key where search starts, zero for root.
02447 @return CM_SUCCESS
02448 */
02449 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02450 {
02451    if (_hDB) {
02452       if (hDB != NULL)
02453          *hDB = _hDB;
02454       if (hKeyClient != NULL)
02455          *hKeyClient = _hKeyClient;
02456    } else {
02457       if (hDB != NULL)
02458          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02459       if (hKeyClient != NULL)
02460          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02461    }
02462 
02463    return CM_SUCCESS;
02464 }
02465 
02466 /**dox***************************************************************/
02467 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02468 
02469 /********************************************************************/
02470 INT cm_get_experiment_semaphore(INT * semaphore_alarm, INT * semaphore_elog, INT * semaphore_history, INT * semaphore_msg)
02471 /********************************************************************\
02472 
02473   Routine: cm_get_experiment_semaphore
02474 
02475   Purpose: Get the handle to the experiment wide semaphores
02476 
02477   Input:
02478     none
02479 
02480   Output:
02481     INT    semaphore_alarm      Alarm semaphore
02482     INT    semaphore_elog       Elog semaphore
02483     INT    semaphore_history    History semaphore
02484     INT    semaphore_msg        Message semaphore
02485 
02486   Function value:
02487     CM_SUCCESS              Successful completion
02488 
02489 \********************************************************************/
02490 {
02491    if (semaphore_alarm)
02492       *semaphore_alarm = _semaphore_alarm;
02493    if (semaphore_elog)
02494       *semaphore_elog = _semaphore_elog;
02495    if (semaphore_history)
02496       *semaphore_history = _semaphore_history;
02497    if (semaphore_msg)
02498       *semaphore_msg = _semaphore_msg;
02499 
02500    return CM_SUCCESS;
02501 }
02502 
02503 /**dox***************************************************************/
02504 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02505 
02506 static int bm_validate_client_index(const BUFFER * buf)
02507 {
02508    int badindex = 0;
02509    BUFFER_CLIENT *bcl = buf->buffer_header->client;
02510 
02511    if (buf->client_index < 0)
02512       badindex = 1;
02513    else if (buf->client_index > buf->buffer_header->max_client_index)
02514       badindex = 1;
02515    else {
02516       bcl = &(buf->buffer_header->client[buf->client_index]);
02517       if (bcl->name[0] == 0)
02518          badindex = 1;
02519       else if (bcl->pid != ss_getpid())
02520          badindex = 1;
02521    }
02522 
02523 #if 0
02524    printf
02525        ("bm_validate_client_index: badindex=%d, buf=%p, client_index=%d, max_client_index=%d, client_name=\'%s\', client_pid=%d, pid=%d\n",
02526         badindex, buf, buf->client_index, buf->buffer_header->max_client_index,
02527         buf->buffer_header->client[buf->client_index].name, buf->buffer_header->client[buf->client_index].pid,
02528         ss_getpid());
02529 #endif
02530 
02531    if (badindex) {
02532       static int prevent_recursion = 1;
02533       if (prevent_recursion) {
02534          prevent_recursion = 0;
02535          cm_msg(MERROR, "bm_validate_client_index",
02536                 "Invalid client index %d in buffer \'%s\'. Client name \'%s\', pid %d should be %d",
02537                 buf->client_index, buf->buffer_header->name, bcl->name, bcl->pid, ss_getpid());
02538          cm_msg(MERROR, "bm_validate_client_index",
02539                 "Maybe this client was removed by a timeout. Cannot continue, exiting.");
02540       }
02541       exit(1);
02542    }
02543 
02544    return buf->client_index;
02545 }
02546 
02547 /********************************************************************/
02548 /**
02549 Sets the internal watchdog flags and the own timeout.
02550 If call_watchdog is TRUE, the cm_watchdog routine is called
02551 periodically from the system to show other clients that
02552 this application is "alive". On UNIX systems, the
02553 alarm() timer is used which is then not available for
02554 user purposes.
02555 
02556 The timeout specifies the time, after which the calling
02557 application should be considered "dead" by other clients.
02558 Normally, the cm_watchdog() routines is called periodically.
02559 If a client crashes, this does not occur any more. Then
02560 other clients can detect this and clear all buffer and
02561 database entries of this application so they are not
02562 blocked any more. If this application should not checked
02563 by others, the timeout can be specified as zero.
02564 It might be useful for debugging purposes to do so,
02565 because if a debugger comes to a breakpoint and stops
02566 the application, the periodic call of cm_watchdog
02567 is disabled and the client looks like dead.
02568 
02569 If the timeout is not zero, but the watchdog is not
02570 called (call_watchdog == FALSE), the user must ensure
02571 to call cm_watchdog periodically with a period of
02572 WATCHDOG_INTERVAL milliseconds or less.
02573 
02574 An application which calles system routines which block
02575 the alarm signal for some time, might increase the
02576 timeout to the maximum expected blocking time before
02577 issuing the calls. One example is the logger doing
02578 Exabyte tape IO, which can take up to one minute.
02579 @param    call_watchdog   Call the cm_watchdog routine periodically
02580 @param    timeout         Timeout for this application in ms
02581 @return   CM_SUCCESS
02582 */
02583 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
02584 {
02585    INT i;
02586 
02587    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
02588    _watchdog_timeout = timeout;
02589 
02590    if (rpc_is_remote())
02591       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
02592 
02593 #ifdef LOCAL_ROUTINES
02594 
02595    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
02596       HNDLE hDB, hKey;
02597 
02598       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
02599 
02600       /* write timeout value to client enty in ODB */
02601       cm_get_experiment_database(&hDB, &hKey);
02602 
02603       if (hDB) {
02604          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02605          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
02606          db_set_mode(hDB, hKey, MODE_READ, TRUE);
02607       }
02608    } else {
02609       _call_watchdog = call_watchdog;
02610       _watchdog_timeout = timeout;
02611 
02612       /* set watchdog flag of all open buffers */
02613       for (i = _buffer_entries; i > 0; i--) {
02614          BUFFER_CLIENT *pclient;
02615          BUFFER_HEADER *pheader;
02616          INT idx;
02617 
02618          idx = bm_validate_client_index(&_buffer[i - 1]);
02619          pheader = _buffer[i - 1].buffer_header;
02620          pclient = &pheader->client[idx];
02621 
02622          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE
02623              && _buffer[i - 1].index != rpc_get_server_acception())
02624             continue;
02625 
02626          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i - 1].index != ss_gettid())
02627             continue;
02628 
02629          if (!_buffer[i - 1].attached)
02630             continue;
02631 
02632          /* clear entry from client structure in buffer header */
02633          pclient->watchdog_timeout = timeout;
02634 
02635          /* show activity */
02636          pclient->last_activity = ss_millitime();
02637       }
02638 
02639       /* set watchdog flag of alll open databases */
02640       for (i = _database_entries; i > 0; i--) {
02641          DATABASE_HEADER *pheader;
02642          DATABASE_CLIENT *pclient;
02643          INT idx;
02644 
02645          db_lock_database(i);
02646          idx = _database[i - 1].client_index;
02647          pheader = _database[i - 1].database_header;
02648          pclient = &pheader->client[idx];
02649 
02650          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02651              _database[i - 1].index != rpc_get_server_acception()) {
02652             db_unlock_database(i);
02653             continue;
02654          }
02655 
02656          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _database[i - 1].index != ss_gettid()) {
02657             db_unlock_database(i);
02658             continue;
02659          }
02660 
02661          if (!_database[i - 1].attached) {
02662             db_unlock_database(i);
02663             continue;
02664          }
02665 
02666          /* clear entry from client structure in buffer header */
02667          pclient->watchdog_timeout = timeout;
02668 
02669          /* show activity */
02670          pclient->last_activity = ss_millitime();
02671 
02672          db_unlock_database(i);
02673       }
02674 
02675       if (call_watchdog)
02676          /* restart watchdog */
02677          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02678       else
02679          /* kill current timer */
02680          ss_alarm(0, cm_watchdog);
02681    }
02682 
02683 #endif                          /* LOCAL_ROUTINES */
02684 
02685    return CM_SUCCESS;
02686 }
02687 
02688 /********************************************************************/
02689 /**
02690 Return the current watchdog parameters
02691 @param call_watchdog   Call the cm_watchdog routine periodically
02692 @param timeout         Timeout for this application in seconds
02693 @return   CM_SUCCESS
02694 */
02695 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
02696 {
02697    if (call_watchdog)
02698       *call_watchdog = _call_watchdog;
02699    if (timeout)
02700       *timeout = _watchdog_timeout;
02701 
02702    return CM_SUCCESS;
02703 }
02704 
02705 /********************************************************************/
02706 /**
02707 Return watchdog information about specific client
02708 @param    hDB              ODB handle
02709 @param    client_name     ODB client name
02710 @param    timeout         Timeout for this application in seconds
02711 @param    last            Last time watchdog was called in msec
02712 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE
02713 */
02714 
02715 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
02716 {
02717    if (rpc_is_remote())
02718       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
02719 
02720 #ifdef LOCAL_ROUTINES
02721    {
02722       DATABASE_HEADER *pheader;
02723       DATABASE_CLIENT *pclient;
02724       INT i;
02725 
02726       if (hDB > _database_entries || hDB <= 0) {
02727          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02728          return DB_INVALID_HANDLE;
02729       }
02730 
02731       if (!_database[hDB - 1].attached) {
02732          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02733          return DB_INVALID_HANDLE;
02734       }
02735 
02736       /* lock database */
02737       db_lock_database(hDB);
02738 
02739       pheader = _database[hDB - 1].database_header;
02740       pclient = pheader->client;
02741 
02742       /* find client */
02743       for (i = 0; i < pheader->max_client_index; i++, pclient++)
02744          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
02745             *timeout = pclient->watchdog_timeout;
02746             *last = ss_millitime() - pclient->last_activity;
02747             db_unlock_database(hDB);
02748             return CM_SUCCESS;
02749          }
02750 
02751       *timeout = *last = 0;
02752 
02753       db_unlock_database(hDB);
02754 
02755       return CM_NO_CLIENT;
02756    }
02757 #else                           /* LOCAL_ROUTINES */
02758    return CM_SUCCESS;
02759 #endif                          /* LOCAL_ROUTINES */
02760 }
02761 
02762 
02763 /**dox***************************************************************/
02764 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02765 
02766 /********************************************************************/
02767 INT cm_register_server(void)
02768 /********************************************************************\
02769 
02770   Routine: cm_register_server
02771 
02772   Purpose: Register a server which can be called from other clients
02773            of a specific experiment.
02774 
02775   Input:
02776     none
02777 
02778   Output:
02779     none
02780 
02781   Function value:
02782     CM_SUCCESS              Successful completion
02783 
02784 \********************************************************************/
02785 {
02786    INT status, port;
02787    HNDLE hDB, hKey;
02788 
02789    if (!_server_registered) {
02790       port = 0;
02791       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
02792       if (status != RPC_SUCCESS)
02793          return status;
02794       _server_registered = TRUE;
02795 
02796       /* register MIDAS library functions */
02797       rpc_register_functions(rpc_get_internal_list(1), NULL);
02798 
02799       /* store port number in ODB */
02800       cm_get_experiment_database(&hDB, &hKey);
02801 
02802       status = db_find_key(hDB, hKey, "Server Port", &hKey);
02803       if (status != DB_SUCCESS)
02804          return status;
02805 
02806       /* unlock database */
02807       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02808 
02809       /* set value */
02810       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
02811       if (status != DB_SUCCESS)
02812          return status;
02813 
02814       /* lock database */
02815       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02816    }
02817 
02818    return CM_SUCCESS;
02819 }
02820 
02821 /**dox***************************************************************/
02822 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02823 
02824 /********************************************************************/
02825 /**
02826 Registers a callback function for run transitions.
02827 This function internally registers the transition callback
02828 function and publishes its request for transition notification by writing
02829 a transition request to /System/Clients/<pid>/Transition XXX.
02830 Other clients making a transition scan the transition requests of all clients
02831 and call their transition callbacks via RPC.
02832 
02833 Clients can register for transitions (Start/Stop/Pause/Resume) in a given
02834 sequence. All sequence numbers given in the registration are sorted on
02835 a transition and the clients are contacted in ascending order. By default,
02836 all programs register with a sequence number of 500. The logger however
02837 uses 200 for start, so that it can open files before the other clients
02838 are contacted, and 800 for stop, so that the files get closed when all
02839 other clients have gone already through the stop trantition.
02840 
02841 The callback function returns CM_SUCCESS if it can perform the transition or
02842 a value larger than one in case of error. An error string can be copied
02843 into the error variable.
02844 @attention The callback function will be called on transitions from inside the
02845     cm_yield() function which therefore must be contained in the main program loop.
02846 \code
02847 INT start(INT run_number, char *error)
02848 {
02849   if (<not ok>)
02850     {
02851     strcpy(error, "Cannot start because ...");
02852     return 2;
02853     }
02854   printf("Starting run %d\n", run_number);
02855   return CM_SUCCESS;
02856 }
02857 main()
02858 {
02859   ...
02860   cm_register_transition(TR_START, start, 500);
02861   do
02862     {
02863     status = cm_yield(1000);
02864     } while (status != RPC_SHUTDOWN &&
02865              status != SS_ABORT);
02866   ...
02867 }
02868 \endcode
02869 @param transition Transition to register for (see @ref state_transition)
02870 @param func Callback function.
02871 @param sequence_number Sequence number for that transition (1..1000)
02872 @return CM_SUCCESS
02873 */
02874 INT cm_register_transition(INT transition, INT(*func) (INT, char *), INT sequence_number)
02875 {
02876    INT status, i;
02877    HNDLE hDB, hKey, hKeyTrans;
02878    KEY key;
02879    char str[256];
02880 
02881    /* check for valid transition */
02882    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME
02883        && transition != TR_STARTABORT) {
02884       cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
02885       return CM_INVALID_TRANSITION;
02886    }
02887 
02888    cm_get_experiment_database(&hDB, &hKey);
02889 
02890    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
02891 
02892    /* register new transition request */
02893 
02894    /* find empty slot */
02895    for (i = 0; i < MAX_TRANSITIONS; i++)
02896       if (!_trans_table[i].transition)
02897          break;
02898 
02899    if (i == MAX_TRANSITIONS) {
02900       cm_msg(MERROR, "cm_register_transition",
02901              "To many transition registrations. Please increase MAX_TRANSITIONS and recompile");
02902       return CM_TOO_MANY_REQUESTS;
02903    }
02904 
02905    _trans_table[i].transition = transition;
02906    _trans_table[i].func = func;
02907    _trans_table[i].sequence_number = sequence_number;
02908 
02909    for (i = 0;; i++)
02910       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
02911          break;
02912 
02913    sprintf(str, "Transition %s", trans_name[i].name);
02914 
02915    /* unlock database */
02916    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02917 
02918    /* set value */
02919    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02920    if (!hKeyTrans) {
02921       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02922       if (status != DB_SUCCESS)
02923          return status;
02924    } else {
02925       status = db_get_key(hDB, hKeyTrans, &key);
02926       if (status != DB_SUCCESS)
02927          return status;
02928       status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT);
02929       if (status != DB_SUCCESS)
02930          return status;
02931    }
02932 
02933    /* re-lock database */
02934    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02935 
02936    return CM_SUCCESS;
02937 }
02938 
02939 INT cm_deregister_transition(INT transition)
02940 {
02941    INT status, i;
02942    HNDLE hDB, hKey, hKeyTrans;
02943    char str[256];
02944 
02945    /* check for valid transition */
02946    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
02947       cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
02948       return CM_INVALID_TRANSITION;
02949    }
02950 
02951    cm_get_experiment_database(&hDB, &hKey);
02952 
02953    /* remove existing transition request */
02954    for (i = 0; i < MAX_TRANSITIONS; i++)
02955       if (_trans_table[i].transition == transition)
02956          break;
02957 
02958    if (i == MAX_TRANSITIONS) {
02959       cm_msg(MERROR, "cm_register_transition",
02960              "Cannot de-register transition registration, request not found");
02961       return CM_INVALID_TRANSITION;
02962    }
02963 
02964    _trans_table[i].transition = 0;
02965    _trans_table[i].func = NULL;
02966    _trans_table[i].sequence_number = 0;
02967 
02968    for (i = 0;; i++)
02969       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
02970          break;
02971 
02972    sprintf(str, "Transition %s", trans_name[i].name);
02973 
02974    /* unlock database */
02975    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02976 
02977    /* set value */
02978    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02979    if (hKeyTrans) {
02980       status = db_delete_key(hDB, hKeyTrans, FALSE);
02981       if (status != DB_SUCCESS)
02982          return status;
02983    }
02984 
02985    /* re-lock database */
02986    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02987 
02988    return CM_SUCCESS;
02989 }
02990 
02991 /********************************************************************/
02992 /**
02993 Change the transition sequence for the calling program.
02994 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02995 @param sequence_number New sequence number, should be between 1 and 1000
02996 @return     CM_SUCCESS
02997 */
02998 INT cm_set_transition_sequence(INT transition, INT sequence_number)
02999 {
03000    INT status, i;
03001    HNDLE hDB, hKey;
03002    char str[256];
03003 
03004    /* check for valid transition */
03005    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
03006       cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
03007       return CM_INVALID_TRANSITION;
03008    }
03009 
03010    cm_get_experiment_database(&hDB, &hKey);
03011 
03012    /* Find the transition type from the list */
03013    for (i = 0;; i++)
03014       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
03015          break;
03016    sprintf(str, "Transition %s", trans_name[i].name);
03017 
03018    /* Change local sequence number for this transition type */
03019    for (i = 0; i < MAX_TRANSITIONS; i++)
03020       if (_trans_table[i].transition == transition) {
03021          _trans_table[i].sequence_number = sequence_number;
03022          break;
03023       }
03024 
03025    /* unlock database */
03026    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03027 
03028    /* set value */
03029    status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
03030    if (status != DB_SUCCESS)
03031       return status;
03032 
03033    /* re-lock database */
03034    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03035 
03036    return CM_SUCCESS;
03037 
03038 }
03039 
03040 /**dox***************************************************************/
03041 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03042 
03043 static INT _requested_transition;
03044 static DWORD _deferred_transition_mask;
03045 
03046 /**dox***************************************************************/
03047 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03048 
03049 /********************************************************************/
03050 /**
03051 Register a deferred transition handler. If a client is
03052 registered as a deferred transition handler, it may defer
03053 a requested transition by returning FALSE until a certain
03054 condition (like a motor reaches its end position) is
03055 reached.
03056 @param transition      One of TR_xxx
03057 @param (*func)         Function which gets called whenever
03058                        a transition is requested. If it returns
03059                        FALSE, the transition is not performed.
03060 @return CM_SUCCESS,    <error> Error from ODB access
03061 */
03062 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
03063 {
03064    INT status, i, size;
03065    char tr_key_name[256];
03066    HNDLE hDB, hKey;
03067 
03068    cm_get_experiment_database(&hDB, &hKey);
03069 
03070    for (i = 0; _deferred_trans_table[i].transition; i++)
03071       if (_deferred_trans_table[i].transition == transition)
03072          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
03073 
03074    /* set new transition mask */
03075    _deferred_transition_mask |= transition;
03076 
03077    for (i = 0;; i++)
03078       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
03079          break;
03080 
03081    sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
03082 
03083    /* unlock database */
03084    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03085 
03086    /* set value */
03087    i = 0;
03088    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT);
03089    if (status != DB_SUCCESS)
03090       return status;
03091 
03092    /* re-lock database */
03093    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03094 
03095    /* hot link requested transition */
03096    size = sizeof(_requested_transition);
03097    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT, TRUE);
03098    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
03099    status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
03100    if (status != DB_SUCCESS) {
03101       cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
03102       return status;
03103    }
03104 
03105    return CM_SUCCESS;
03106 }
03107 
03108 /********************************************************************/
03109 /**
03110 Check for any deferred transition. If a deferred transition
03111 handler has been registered via the
03112 cm_register_deferred_transition function, this routine
03113 should be called regularly. It checks if a transition
03114 request is pending. If so, it calld the registered handler
03115 if the transition should be done and then actually does
03116 the transition.
03117 @return     CM_SUCCESS, <error>  Error from cm_transition()
03118 */
03119 INT cm_check_deferred_transition()
03120 {
03121    INT i, status;
03122    char str[256];
03123    static BOOL first;
03124 
03125    if (_requested_transition == 0)
03126       first = TRUE;
03127 
03128    if (_requested_transition & _deferred_transition_mask) {
03129       for (i = 0; _deferred_trans_table[i].transition; i++)
03130          if (_deferred_trans_table[i].transition == _requested_transition)
03131             break;
03132 
03133       if (_deferred_trans_table[i].transition == _requested_transition) {
03134          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition, first)) {
03135             status = cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str), SYNC, FALSE);
03136             if (status != CM_SUCCESS)
03137                cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
03138 
03139             /* bypass hotlink and set _requested_transition directly to zero */
03140             _requested_transition = 0;
03141 
03142             return status;
03143          }
03144          first = FALSE;
03145       }
03146    }
03147 
03148    return SUCCESS;
03149 }
03150 
03151 
03152 /**dox***************************************************************/
03153 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03154 
03155 /********************************************************************/
03156 
03157 /**dox***************************************************************/
03158 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03159 
03160 typedef struct {
03161    int sequence_number;
03162    char host_name[HOST_NAME_LENGTH];
03163    char client_name[NAME_LENGTH];
03164    int port;
03165 } TR_CLIENT;
03166 
03167 int tr_compare(const void *arg1, const void *arg2)
03168 {
03169    return ((TR_CLIENT *) arg1)->sequence_number - ((TR_CLIENT *) arg2)->sequence_number;
03170 }
03171 
03172 /********************************************************************/
03173 /**
03174 Performs a run transition (Start/Stop/Pause/Resume).
03175 
03176 Synchronous/Asynchronous flag.
03177 If set to ASYNC, the transition is done
03178 asynchronously, meaning that clients are connected and told to execute their
03179 callback routine, but no result is awaited. The return value is
03180 specified by the transition callback function on the remote clients. If all callbacks
03181 can perform the transition, CM_SUCCESS is returned. If one callback cannot
03182 perform the transition, the return value of this callback is returned from
03183 cm_transition().
03184 The async_flag is usually FALSE so that transition callbacks can block a
03185 run transition in case of problems and return an error string. The only exception are
03186 situations where a run transition is performed automatically by a program which
03187 cannot block in a transition. For example the logger can cause a run stop when a
03188 disk is nearly full but it cannot block in the cm_transition() function since it
03189 has its own run stop callback which must flush buffers and close disk files and
03190 tapes.
03191 \code
03192 ...
03193     i = 1;
03194     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03195 
03196       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
03197       if (status != CM_SUCCESS)
03198       {
03199         // in case of error
03200         printf("Error: %s\n", str);
03201       }
03202     ...
03203 \endcode
03204 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03205 @param run_number New run number. If zero, use current run number plus one.
03206 @param errstr returned error string.
03207 @param errstr_size Size of error string.
03208 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
03209 @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
03210 @return CM_SUCCESS, <error> error code from remote client
03211 */
03212 INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag,
03213                    INT debug_flag)
03214 {
03215    INT i, j, status, idx, size, sequence_number, port, state, old_timeout, n_tr_clients;
03216    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hConn, hKeyTrans;
03217    DWORD seconds;
03218    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH], str[256], error[256], tr_key_name[256];
03219    char *trname = "unknown";
03220    KEY key;
03221    BOOL deferred;
03222    PROGRAM_INFO program_info;
03223    TR_CLIENT *tr_client;
03224    int connect_timeout = 10000;
03225    int timeout = 120000;
03226    int t0, t1;
03227 
03228    deferred = (transition & TR_DEFERRED) > 0;
03229    transition &= ~TR_DEFERRED;
03230 
03231    /* check for valid transition */
03232    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME
03233        && transition != TR_STARTABORT) {
03234       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03235       if (errstr != NULL)
03236          strlcpy(errstr, "Invalid transition request", errstr_size);
03237       return CM_INVALID_TRANSITION;
03238    }
03239 
03240    /* get key of local client */
03241    cm_get_experiment_database(&hDB, &hKeylocal);
03242 
03243    if (errstr != NULL)
03244       strlcpy(errstr, "Unknown error", errstr_size);
03245 
03246    if (debug_flag == 0) {
03247       size = sizeof(i);
03248       db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT, TRUE);
03249    }
03250 
03251    /* do detached transition via mtransition tool */
03252    if (async_flag == DETACH) {
03253       char *args[100];
03254       int iarg = 0;
03255       char debug_arg[256];
03256       char start_arg[256];
03257       char expt_name[256];
03258       extern RPC_SERVER_CONNECTION _server_connection;
03259 
03260       args[iarg++] = "mtransition";
03261 
03262       if (_server_connection.send_sock) {
03263          /* if connected to mserver, pass connection info to mtransition */
03264          args[iarg++] = "-h";
03265          args[iarg++] = _server_connection.host_name;
03266          args[iarg++] = "-e";
03267          args[iarg++] = _server_connection.exp_name;
03268       } else {
03269          /* get experiment name from ODB */
03270          size = sizeof(expt_name);
03271          db_get_value(hDB, 0, "/Experiment/Name", expt_name, &size, TID_STRING, FALSE);
03272 
03273          args[iarg++] = "-e";
03274          args[iarg++] = expt_name;
03275       }
03276 
03277       if (debug_flag) {
03278          args[iarg++] = "-d";
03279 
03280          sprintf(debug_arg, "%d", debug_flag);
03281          args[iarg++] = debug_arg;
03282       }
03283 
03284       if (transition == TR_STOP)
03285          args[iarg++] = "STOP";
03286       else if (transition == TR_PAUSE)
03287          args[iarg++] = "PAUSE";
03288       else if (transition == TR_RESUME)
03289          args[iarg++] = "RESUME";
03290       else if (transition == TR_START) {
03291          args[iarg++] = "START";
03292 
03293          sprintf(start_arg, "%d", run_number);
03294          args[iarg++] = start_arg;
03295       }
03296 
03297       args[iarg++] = NULL;
03298 
03299       if (0)
03300          for (iarg = 0; args[iarg] != NULL; iarg++)
03301             printf("arg[%d] [%s]\n", iarg, args[iarg]);
03302 
03303       status = ss_spawnv(P_DETACH, args[0], args);
03304 
03305       if (status != SUCCESS) {
03306          if (errstr != NULL)
03307             sprintf(errstr, "Cannot execute mtransition, ss_spawnvp() returned %d", status);
03308          return CM_SET_ERROR;
03309       }
03310 
03311       return CM_SUCCESS;
03312    }
03313 
03314    /* if no run number is given, get it from DB */
03315    if (run_number == 0) {
03316       size = sizeof(run_number);
03317       status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
03318       assert(status == SUCCESS);
03319    }
03320 
03321    if (run_number <= 0) {
03322       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
03323       abort();
03324    }
03325 
03326    /* check if transition in progress */
03327    if (!deferred) {
03328       i = 0;
03329       size = sizeof(i);
03330       db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT, TRUE);
03331       if (i == 1) {
03332          sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
03333          strlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
03334          return CM_TRANSITION_IN_PROGRESS;
03335       }
03336    }
03337 
03338    /* get transition timeout for rpc connect */
03339    size = sizeof(timeout);
03340    db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT, TRUE);
03341 
03342    if (connect_timeout < 1000)
03343       connect_timeout = 1000;
03344 
03345    /* get transition timeout */
03346    size = sizeof(timeout);
03347    db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT, TRUE);
03348 
03349    if (timeout < 1000)
03350       timeout = 1000;
03351 
03352    /* indicate transition in progress */
03353    i = transition;
03354    db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03355 
03356    /* clear run abort flag */
03357    i = 0;
03358    db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03359 
03360    /* Set new run number in ODB */
03361    if (transition == TR_START) {
03362       if (debug_flag == 1)
03363          printf("Setting run number %d in ODB\n", run_number);
03364       if (debug_flag == 2)
03365          cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
03366 
03367       status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT);
03368       assert(status == SUCCESS);
03369       if (status != DB_SUCCESS)
03370          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
03371    }
03372 
03373    if (deferred) {
03374       /* remove transition request */
03375       i = 0;
03376       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
03377    } else {
03378       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03379       if (status != DB_SUCCESS) {
03380          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03381          if (errstr != NULL)
03382             strlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
03383          return status;
03384       }
03385 
03386       /* check if deferred transition already in progress */
03387       size = sizeof(i);
03388       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
03389       if (i) {
03390          if (errstr != NULL)
03391             strlcpy(errstr, "Deferred transition already in progress", errstr_size);
03392          return CM_TRANSITION_IN_PROGRESS;
03393       }
03394 
03395       for (i = 0; trans_name[i].name[0] != 0; i++)
03396          if (trans_name[i].transition == transition) {
03397             trname = trans_name[i].name;
03398             break;
03399          }
03400 
03401       sprintf(tr_key_name, "Transition %s DEFERRED", trname);
03402 
03403       /* search database for clients with deferred transition request */
03404       for (i = 0, status = 0;; i++) {
03405          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03406          if (status == DB_NO_MORE_SUBKEYS)
03407             break;
03408 
03409          if (status == DB_SUCCESS) {
03410             size = sizeof(sequence_number);
03411             status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT, FALSE);
03412 
03413             /* if registered for deferred transition, set flag in ODB and return */
03414             if (status == DB_SUCCESS) {
03415                size = NAME_LENGTH;
03416                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03417                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT);
03418 
03419                if (debug_flag == 1)
03420                   printf("---- Transition %s deferred by client \"%s\" ----\n", trname, str);
03421                if (debug_flag == 2)
03422                   cm_msg(MINFO, "cm_transition",
03423                          "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname, str);
03424 
03425                if (errstr)
03426                   sprintf(errstr, "Transition %s deferred by client \"%s\"", trname, str);
03427 
03428                return CM_DEFERRED_TRANSITION;
03429             }
03430          }
03431       }
03432    }
03433 
03434    /* execute programs on start */
03435    if (transition == TR_START) {
03436       str[0] = 0;
03437       size = sizeof(str);
03438       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
03439       if (str[0])
03440          ss_system(str);
03441 
03442       db_find_key(hDB, 0, "/Programs", &hRootKey);
03443       if (hRootKey) {
03444          for (i = 0;; i++) {
03445             status = db_enum_key(hDB, hRootKey, i, &hKey);
03446             if (status == DB_NO_MORE_SUBKEYS)
03447                break;
03448 
03449             db_get_key(hDB, hKey, &key);
03450 
03451             /* don't check "execute on xxx" */
03452             if (key.type != TID_KEY)
03453                continue;
03454 
03455             size = sizeof(program_info);
03456             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03457             if (status != DB_SUCCESS) {
03458                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03459                continue;
03460             }
03461 
03462             if (program_info.auto_start && program_info.start_command[0])
03463                ss_system(program_info.start_command);
03464          }
03465       }
03466    }
03467 
03468    /* set new start time in database */
03469    if (transition == TR_START) {
03470       /* ASCII format */
03471       cm_asctime(str, sizeof(str));
03472       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03473 
03474       /* reset stop time */
03475       seconds = 0;
03476       db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03477 
03478       /* Seconds since 1.1.1970 */
03479       cm_time(&seconds);
03480       db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03481    }
03482 
03483    /* set stop time in database */
03484    if (transition == TR_STOP) {
03485       size = sizeof(state);
03486       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03487       if (status != DB_SUCCESS)
03488          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03489 
03490       if (state != STATE_STOPPED) {
03491          /* stop time binary */
03492          cm_time(&seconds);
03493          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03494          if (status != DB_SUCCESS)
03495             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
03496 
03497          /* stop time ascii */
03498          cm_asctime(str, sizeof(str));
03499          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03500          if (status != DB_SUCCESS)
03501             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
03502       }
03503    }
03504 
03505    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03506    if (status != DB_SUCCESS) {
03507       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03508       if (errstr)
03509          strlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
03510       return status;
03511    }
03512 
03513    for (i = 0; trans_name[i].name[0] != 0; i++)
03514       if (trans_name[i].transition == transition) {
03515          trname = trans_name[i].name;
03516          break;
03517       }
03518 
03519    /* check that all transition clients are alive */
03520    for (i = 0;;) {
03521       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03522       if (status != DB_SUCCESS)
03523          break;
03524 
03525       status = cm_check_client(hDB, hSubkey);
03526 
03527       if (status == DB_SUCCESS) {
03528          /* this client is alive. Check next one! */
03529          i++;
03530          continue;
03531       }
03532 
03533       assert(status == CM_NO_CLIENT);
03534 
03535       /* start from scratch: removing odb entries as we iterate over them
03536        * does strange things to db_enum_key() */
03537       i = 0;
03538    }
03539 
03540    if (debug_flag == 1)
03541       printf("---- Transition %s started ----\n", trname);
03542    if (debug_flag == 2)
03543       cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname);
03544 
03545    sprintf(tr_key_name, "Transition %s", trname);
03546 
03547    /* search database for clients which registered for transition */
03548    n_tr_clients = 0;
03549    tr_client = NULL;
03550 
03551    for (i = 0, status = 0;; i++) {
03552       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03553       if (status == DB_NO_MORE_SUBKEYS)
03554          break;
03555 
03556       if (status == DB_SUCCESS) {
03557          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
03558 
03559          if (status == DB_SUCCESS) {
03560 
03561             db_get_key(hDB, hKeyTrans, &key);
03562 
03563             for (j = 0; j < key.num_values; j++) {
03564                size = sizeof(sequence_number);
03565                status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT);
03566                assert(status == DB_SUCCESS);
03567 
03568                if (tr_client == NULL)
03569                   tr_client = (TR_CLIENT *) malloc(sizeof(TR_CLIENT));
03570                else
03571                   tr_client = (TR_CLIENT *) realloc(tr_client, sizeof(TR_CLIENT) * (n_tr_clients + 1));
03572                assert(tr_client);
03573 
03574                tr_client[n_tr_clients].sequence_number = sequence_number;
03575 
03576                if (hSubkey == hKeylocal) {
03577                   /* remember own client */
03578                   tr_client[n_tr_clients].port = 0;
03579                } else {
03580                   /* get client info */
03581                   size = sizeof(client_name);
03582                   db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
03583                   strcpy(tr_client[n_tr_clients].client_name, client_name);
03584 
03585                   size = sizeof(port);
03586                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03587                   tr_client[n_tr_clients].port = port;
03588 
03589                   size = sizeof(host_name);
03590                   db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03591                   strcpy(tr_client[n_tr_clients].host_name, host_name);
03592                }
03593 
03594                n_tr_clients++;
03595             }
03596          }
03597       }
03598    }
03599 
03600    /* sort clients according to sequence number */
03601    if (n_tr_clients > 1)
03602       qsort(tr_client, n_tr_clients, sizeof(TR_CLIENT), tr_compare);
03603 
03604    /* contact ordered clients for transition */
03605    for (idx = 0; idx < n_tr_clients; idx++) {
03606       /* erase error string */
03607       error[0] = 0;
03608 
03609       if (debug_flag == 1)
03610          printf("\n==== Found client \"%s\" with sequence number %d\n",
03611                 tr_client[idx].client_name, tr_client[idx].sequence_number);
03612       if (debug_flag == 2)
03613          cm_msg(MINFO, "cm_transition",
03614                 "cm_transition: ==== Found client \"%s\" with sequence number %d",
03615                 tr_client[idx].client_name, tr_client[idx].sequence_number);
03616 
03617       /* if own client call transition callback directly */
03618       if (tr_client[idx].port == 0) {
03619          for (i = 0; _trans_table[i].transition; i++)
03620             if (_trans_table[i].transition == transition)
03621                break;
03622 
03623          /* call registered function */
03624          if (_trans_table[i].transition == transition && _trans_table[i].func) {
03625             if (debug_flag == 1)
03626                printf("Calling local transition callback\n");
03627             if (debug_flag == 2)
03628                cm_msg(MINFO, "cm_transition", "cm_transition: Calling local transition callback");
03629 
03630             status = _trans_table[i].func(run_number, error);
03631 
03632             if (debug_flag == 1)
03633                printf("Local transition callback finished\n");
03634             if (debug_flag == 2)
03635                cm_msg(MINFO, "cm_transition", "cm_transition: Local transition callback finished");
03636          } else
03637             status = CM_SUCCESS;
03638 
03639          if (errstr != NULL)
03640             memcpy(errstr, error,
03641                    (INT) strlen(error) + 1 < errstr_size ? (INT) strlen(error) + 1 : errstr_size);
03642 
03643          if (status != CM_SUCCESS) {
03644             /* indicate abort */
03645             i = 1;
03646             db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03647             i = 0;
03648             db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03649 
03650             free(tr_client);
03651             return status;
03652          }
03653 
03654       } else {
03655 
03656          /* contact client if transition mask set */
03657          if (debug_flag == 1)
03658             printf("Connecting to client \"%s\" on host %s...\n", tr_client[idx].client_name,
03659                    tr_client[idx].host_name);
03660          if (debug_flag == 2)
03661             cm_msg(MINFO, "cm_transition",
03662                    "cm_transition: Connecting to client \"%s\" on host %s...",
03663                    tr_client[idx].client_name, tr_client[idx].host_name);
03664 
03665          /* set our timeout for rpc_client_connect() */
03666          old_timeout = rpc_get_option(-2, RPC_OTIMEOUT);
03667          rpc_set_option(-2, RPC_OTIMEOUT, connect_timeout);
03668 
03669          /* client found -> connect to its server port */
03670          status =
03671              rpc_client_connect(tr_client[idx].host_name, tr_client[idx].port, tr_client[idx].client_name,
03672                                 &hConn);
03673 
03674          rpc_set_option(-2, RPC_OTIMEOUT, old_timeout);
03675 
03676          if (status != RPC_SUCCESS) {
03677             cm_msg(MERROR, "cm_transition",
03678                    "cannot connect to client \"%s\" on host %s, port %d, status %d",
03679                    tr_client[idx].client_name, tr_client[idx].host_name, tr_client[idx].port, status);
03680             if (errstr != NULL) {
03681                strlcpy(errstr, "Cannot connect to client \'", errstr_size);
03682                strlcat(errstr, tr_client[idx].client_name, errstr_size);
03683                strlcat(errstr, "\'", errstr_size);
03684             }
03685 
03686             /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
03687             cm_shutdown(tr_client[idx].client_name, TRUE);
03688             cm_cleanup(tr_client[idx].client_name, TRUE);
03689 
03690             /* indicate abort */
03691             i = 1;
03692             db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03693             i = 0;
03694             db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03695 
03696             return status;
03697          }
03698 
03699          if (debug_flag == 1)
03700             printf("Connection established to client \"%s\" on host %s\n",
03701                    tr_client[idx].client_name, tr_client[idx].host_name);
03702          if (debug_flag == 2)
03703             cm_msg(MINFO, "cm_transition",
03704                    "cm_transition: Connection established to client \"%s\" on host %s",
03705                    tr_client[idx].client_name, tr_client[idx].host_name);
03706 
03707          /* call RC_TRANSITION on remote client with increased timeout */
03708          old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03709          rpc_set_option(hConn, RPC_OTIMEOUT, timeout);
03710 
03711          /* set FTPC protocol if in async mode */
03712          if (async_flag == ASYNC)
03713             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03714 
03715          if (debug_flag == 1)
03716             printf("Executing RPC transition client \"%s\" on host %s...\n",
03717                    tr_client[idx].client_name, tr_client[idx].host_name);
03718          if (debug_flag == 2)
03719             cm_msg(MINFO, "cm_transition",
03720                    "cm_transition: Executing RPC transition client \"%s\" on host %s...",
03721                    tr_client[idx].client_name, tr_client[idx].host_name);
03722 
03723          t0 = ss_millitime();
03724 
03725          status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
03726                                   run_number, error, errstr_size, tr_client[idx].sequence_number);
03727 
03728          t1 = ss_millitime();
03729 
03730          /* reset timeout */
03731          rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03732 
03733          /* reset protocol */
03734          if (async_flag == ASYNC)
03735             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
03736 
03737          if (debug_flag == 1)
03738             printf("RPC transition finished client \"%s\" on host %s in %d ms with status %d\n",
03739                    tr_client[idx].client_name, tr_client[idx].host_name, t1 - t0, status);
03740          if (debug_flag == 2)
03741             cm_msg(MINFO, "cm_transition",
03742                    "cm_transition: RPC transition finished client \"%s\" on host %s in %d ms with status %d",
03743                    tr_client[idx].client_name, tr_client[idx].host_name, t1 - t0, status);
03744 
03745          if (errstr != NULL) {
03746             if (strlen(error) < 2)
03747                sprintf(errstr, "Unknown error %d from client \'%s\' on host %s", status,
03748                        tr_client[idx].client_name, tr_client[idx].host_name);
03749             else
03750                memcpy(errstr, error,
03751                       (INT) strlen(error) + 1 < (INT) errstr_size ? (INT) strlen(error) + 1 : errstr_size);
03752          }
03753 
03754          if (status != CM_SUCCESS) {
03755             /* indicate abort */
03756             i = 1;
03757             db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03758             i = 0;
03759             db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03760 
03761             free(tr_client);
03762             return status;
03763          }
03764       }
03765    }
03766 
03767    if (tr_client)
03768       free(tr_client);
03769 
03770    if (debug_flag == 1)
03771       printf("\n---- Transition %s finished ----\n", trname);
03772    if (debug_flag == 2)
03773       cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname);
03774 
03775    /* set new run state in database */
03776    if (transition == TR_START || transition == TR_RESUME)
03777       state = STATE_RUNNING;
03778 
03779    if (transition == TR_PAUSE)
03780       state = STATE_PAUSED;
03781 
03782    if (transition == TR_STOP)
03783       state = STATE_STOPPED;
03784 
03785    if (transition == TR_STARTABORT)
03786       state = STATE_STOPPED;
03787 
03788    size = sizeof(state);
03789    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
03790    if (status != DB_SUCCESS)
03791       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
03792 
03793    /* send notification message */
03794    str[0] = 0;
03795    if (transition == TR_START)
03796       sprintf(str, "Run #%d started", run_number);
03797    if (transition == TR_STOP)
03798       sprintf(str, "Run #%d stopped", run_number);
03799    if (transition == TR_PAUSE)
03800       sprintf(str, "Run #%d paused", run_number);
03801    if (transition == TR_RESUME)
03802       sprintf(str, "Run #%d resumed", run_number);
03803    if (transition == TR_STARTABORT)
03804       sprintf(str, "Run #%d start aborted", run_number);
03805 
03806    if (str[0])
03807       cm_msg(MINFO, "cm_transition", str);
03808 
03809    /* lock/unlock ODB values if present */
03810    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
03811    if (hKey) {
03812       if (state == STATE_STOPPED)
03813          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
03814       else
03815          db_set_mode(hDB, hKey, MODE_READ, TRUE);
03816    }
03817 
03818    /* flush online database */
03819    if (transition == TR_STOP)
03820       db_flush_database(hDB);
03821 
03822    /* execute/stop programs on stop */
03823    if (transition == TR_STOP) {
03824       str[0] = 0;
03825       size = sizeof(str);
03826       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
03827       if (str[0])
03828          ss_system(str);
03829 
03830       db_find_key(hDB, 0, "/Programs", &hRootKey);
03831       if (hRootKey) {
03832          for (i = 0;; i++) {
03833             status = db_enum_key(hDB, hRootKey, i, &hKey);
03834             if (status == DB_NO_MORE_SUBKEYS)
03835                break;
03836 
03837             db_get_key(hDB, hKey, &key);
03838 
03839             /* don't check "execute on xxx" */
03840             if (key.type != TID_KEY)
03841                continue;
03842 
03843             size = sizeof(program_info);
03844             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03845             if (status != DB_SUCCESS) {
03846                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03847                continue;
03848             }
03849 
03850             if (program_info.auto_stop)
03851                cm_shutdown(key.name, FALSE);
03852          }
03853       }
03854    }
03855 
03856 
03857    /* indicate success */
03858    i = 0;
03859    db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03860 
03861    if (errstr != NULL)
03862       strlcpy(errstr, "Success", errstr_size);
03863 
03864    return CM_SUCCESS;
03865 }
03866 
03867 INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag,
03868                   INT debug_flag)
03869 {
03870    int status;
03871 
03872    status = cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
03873 
03874    if (transition == TR_START && status != CM_SUCCESS) {
03875       cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'",
03876              status, errstr);
03877       cm_transition1(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
03878    }
03879 
03880    return status;
03881 }
03882 
03883 
03884 
03885 /**dox***************************************************************/
03886 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03887 
03888 /********************************************************************/
03889 INT cm_dispatch_ipc(char *message, int s)
03890 /********************************************************************\
03891 
03892   Routine: cm_dispatch_ipc
03893 
03894   Purpose: Called from ss_suspend if an IPC message arrives
03895 
03896   Input:
03897     INT   msg               IPC message we got, MSG_ODB/MSG_BM
03898     INT   p1, p2            Optional parameters
03899     int   s                 Optional server socket
03900 
03901   Output:
03902     none
03903 
03904   Function value:
03905     CM_SUCCESS              Successful completion
03906 
03907 \********************************************************************/
03908 {
03909    if (message[0] == 'O') {
03910       HNDLE hDB, hKey;
03911       sscanf(message + 2, "%d %d", &hDB, &hKey);
03912       return db_update_record(hDB, hKey, s);
03913    }
03914 
03915    /* message == "B  " means "resume event sender" */
03916    if (message[0] == 'B' && message[2] != ' ') {
03917       char str[80];
03918 
03919       strcpy(str, message + 2);
03920       if (strchr(str, ' '))
03921          *strchr(str, ' ') = 0;
03922 
03923       if (s)
03924          return bm_notify_client(str, s);
03925       else
03926          return bm_push_event(str);
03927    }
03928 
03929    return CM_SUCCESS;
03930 }
03931 
03932 /********************************************************************/
03933 static BOOL _ctrlc_pressed = FALSE;
03934 
03935 void cm_ctrlc_handler(int sig)
03936 {
03937    int i;
03938 
03939    i = sig;                     /* avoid compiler warning */
03940 
03941    if (_ctrlc_pressed) {
03942       printf("Received 2nd break. Hard abort.\n");
03943       exit(0);
03944    }
03945    printf("Received break. Aborting...\n");
03946    _ctrlc_pressed = TRUE;
03947 
03948    ss_ctrlc_handler(cm_ctrlc_handler);
03949 }
03950 
03951 BOOL cm_is_ctrlc_pressed()
03952 {
03953    return _ctrlc_pressed;
03954 }
03955 
03956 void cm_ack_ctrlc_pressed()
03957 {
03958    _ctrlc_pressed = FALSE;
03959 }
03960 
03961 
03962 /**dox***************************************************************/
03963 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03964 
03965 /********************************************************************/
03966 /**
03967 Central yield functions for clients. This routine should
03968 be called in an infinite loop by a client in order to
03969 give the MIDAS system the opportunity to receive commands
03970 over RPC channels, update database records and receive
03971 events.
03972 @param millisec         Timeout in millisec. If no message is
03973                         received during the specified timeout,
03974                         the routine returns. If millisec=-1,
03975                         it only returns when receiving an
03976                         RPC_SHUTDOWN message.
03977 @return CM_SUCCESS, RPC_SHUTDOWN
03978 */
03979 INT cm_yield(INT millisec)
03980 {
03981    INT status;
03982    BOOL bMore;
03983    static DWORD last_checked = 0;
03984 
03985    /* check for ctrl-c */
03986    if (_ctrlc_pressed)
03987       return RPC_SHUTDOWN;
03988 
03989    /* check for available events */
03990    if (rpc_is_remote()) {
03991       bMore = bm_poll_event(TRUE);
03992       if (bMore)
03993          status = ss_suspend(0, 0);
03994       else
03995          status = ss_suspend(millisec, 0);
03996 
03997       return status;
03998    }
03999 
04000    /* check alarms once every 10 seconds */
04001    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
04002       al_check();
04003       last_checked = ss_time();
04004    }
04005 
04006    bMore = bm_check_buffers();
04007 
04008    if (bMore) {
04009       /* if events available, quickly check other IPC channels */
04010       status = ss_suspend(0, 0);
04011    } else {
04012       /* mark event buffers for ready-to-receive */
04013       bm_mark_read_waiting(TRUE);
04014 
04015       status = ss_suspend(millisec, 0);
04016 
04017       /* unmark event buffers for ready-to-receive */
04018       bm_mark_read_waiting(FALSE);
04019    }
04020 
04021    return status;
04022 }
04023 
04024 /********************************************************************/
04025 /**
04026 Executes command via system() call
04027 @param    command          Command string to execute
04028 @param    result           stdout of command
04029 @param    bufsize          string size in byte
04030 @return   CM_SUCCESS
04031 */
04032 INT cm_execute(const char *command, char *result, INT bufsize)
04033 {
04034    char str[256];
04035    INT n;
04036    int fh;
04037 
04038    if (rpc_is_remote())
04039       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
04040 
04041    if (bufsize > 0) {
04042       strcpy(str, command);
04043       sprintf(str, "%s > %d.tmp", command, ss_getpid());
04044 
04045       system(str);
04046 
04047       sprintf(str, "%d.tmp", ss_getpid());
04048       fh = open(str, O_RDONLY, 0644);
04049       result[0] = 0;
04050       if (fh) {
04051          n = read(fh, result, bufsize - 1);
04052          result[MAX(0, n)] = 0;
04053          close(fh);
04054       }
04055       remove(str);
04056    } else
04057       system(command);
04058 
04059    return CM_SUCCESS;
04060 }
04061 
04062 
04063 
04064 /**dox***************************************************************/
04065 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04066 
04067 /********************************************************************/
04068 INT cm_register_function(INT id, INT(*func) (INT, void **))
04069 /********************************************************************\
04070 
04071   Routine: cm_register_function
04072 
04073   Purpose: Call rpc_register_function and publish the registered
04074            function under system/clients/<pid>/RPC
04075 
04076   Input:
04077     INT      id             RPC ID
04078     INT      *func          New dispatch function
04079 
04080   Output:
04081    <implicit: func gets copied to rpc_list>
04082 
04083   Function value:
04084    CM_SUCCESS               Successful completion
04085    RPC_INVALID_ID           RPC ID not found
04086 
04087 \********************************************************************/
04088 {
04089    HNDLE hDB, hKey;
04090    INT status;
04091    char str[80];
04092 
04093    status = rpc_register_function(id, func);
04094    if (status != RPC_SUCCESS)
04095       return status;
04096 
04097    cm_get_experiment_database(&hDB, &hKey);
04098 
04099    /* create new key for this id */
04100    status = 1;
04101    sprintf(str, "RPC/%d", id);
04102 
04103    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
04104    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
04105    db_set_mode(hDB, hKey, MODE_READ, TRUE);
04106 
04107    if (status != DB_SUCCESS)
04108       return status;
04109 
04110    return CM_SUCCESS;
04111 }
04112 
04113 
04114 /**dox***************************************************************/
04115 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04116 
04117 /**dox***************************************************************/
04118                                                                                                                                /** @} *//* end of cmfunctionc */
04119 
04120 /**dox***************************************************************/
04121 /** @addtogroup bmfunctionc
04122  *
04123  *  @{  */
04124 
04125 /********************************************************************\
04126 *                                                                    *
04127 *                 bm_xxx  -  Buffer Manager Functions                *
04128 *                                                                    *
04129 \********************************************************************/
04130 
04131 /********************************************************************/
04132 /**
04133 Check if an event matches a given event request by the
04134 event id and trigger mask
04135 @param event_id      Event ID of request
04136 @param trigger_mask  Trigger mask of request
04137 @param pevent    Pointer to event to check
04138 @return TRUE      if event matches request
04139 */
04140 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
04141 {
04142    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 || (pevent->event_id & 0xF000) == EVENTID_FRAG)
04143       /* fragmented event */
04144       return ((event_id == EVENTID_ALL ||
04145                event_id == (pevent->event_id & 0x0FFF)) &&
04146               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
04147 
04148    return ((event_id == EVENTID_ALL ||
04149             event_id == pevent->event_id) && (trigger_mask == TRIGGER_ALL
04150                                               || (trigger_mask & pevent->trigger_mask)));
04151 }
04152 
04153 /********************************************************************/
04154 /**
04155 Called to forcibly disconnect given client from a data buffer
04156 */
04157 void bm_remove_client_locked(BUFFER_HEADER * pheader, int j)
04158 {
04159    int k, nc;
04160    BUFFER_CLIENT *pbctmp;
04161 
04162    /* clear entry from client structure in buffer header */
04163    memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04164 
04165    /* calculate new max_client_index entry */
04166    for (k = MAX_CLIENTS - 1; k >= 0; k--)
04167       if (pheader->client[k].pid != 0)
04168          break;
04169    pheader->max_client_index = k + 1;
04170 
04171    /* count new number of clients */
04172    for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04173       if (pheader->client[k].pid != 0)
04174          nc++;
04175    pheader->num_clients = nc;
04176 
04177    /* check if anyone is waiting and wake him up */
04178    pbctmp = pheader->client;
04179 
04180    for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04181       if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04182          ss_resume(pbctmp->port, "B  ");
04183 }
04184 
04185 /********************************************************************/
04186 /**
04187 Check all clients on all buffers, remove invalid clients
04188 */
04189 static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
04190 {
04191    BUFFER_HEADER *pheader;
04192    BUFFER_CLIENT *pbclient;
04193    int i, j;
04194    char str[256];
04195 
04196    /* check buffers */
04197    for (i = 0; i < _buffer_entries; i++)
04198       if (_buffer[i].attached) {
04199          /* update the last_activity entry to show that we are alive */
04200          pheader = _buffer[i].buffer_header;
04201          pbclient = pheader->client;
04202          pbclient[bm_validate_client_index(&_buffer[i])].last_activity = actual_time;
04203 
04204          /* don't check other clients if interval is stange */
04205          if (wrong_interval)
04206             continue;
04207 
04208          /* now check other clients */
04209          for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
04210 
04211 #ifdef OS_UNIX
04212 #ifdef ESRCH
04213             if (pbclient->pid) {
04214                errno = 0;
04215                kill(pbclient->pid, 0);
04216                if (errno == ESRCH) {
04217                   cm_msg(MINFO, "bm_cleanup",
04218                          "Client \'%s\' on buffer \'%s\' removed by %s because client pid %d does not exist",
04219                          pbclient->name, pheader->name, who, pbclient->pid);
04220 
04221                   bm_lock_buffer(i + 1);
04222                   bm_remove_client_locked(pheader, j);
04223                   bm_unlock_buffer(i + 1);
04224                   continue;
04225                }
04226             }
04227 #endif
04228 #endif
04229 
04230             /* If client process has no activity, clear its buffer entry. */
04231             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04232                 actual_time > pbclient->last_activity &&
04233                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04234                bm_lock_buffer(i + 1);
04235                str[0] = 0;
04236 
04237                /* now make again the check with the buffer locked */
04238                actual_time = ss_millitime();
04239                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04240                    actual_time > pbclient->last_activity &&
04241                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04242                   sprintf(str,
04243                           "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs,TO %1.0lfs)",
04244                           pbclient->name, pheader->name, who,
04245                           (actual_time - pbclient->last_activity) / 1000.0,
04246                           pbclient->watchdog_timeout / 1000.0);
04247 
04248                   bm_remove_client_locked(pheader, j);
04249                }
04250 
04251                bm_unlock_buffer(i + 1);
04252 
04253                /* display info message after unlocking buffer */
04254                if (str[0])
04255                   cm_msg(MINFO, "bm_cleanup", str);
04256             }
04257          }
04258       }
04259 }
04260 
04261 /********************************************************************/
04262 /**
04263 Open an event buffer.
04264 Two default buffers are created by the system.
04265 The "SYSTEM" buffer is used to
04266 exchange events and the "SYSMSG" buffer is used to exchange system messages.
04267 The name and size of the event buffers is defined in midas.h as
04268 EVENT_BUFFER_NAME and 2*MAX_EVENT_SIZE.
04269 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
04270 enters a main loop. Events are then received in process_event()
04271 \code
04272 #include <stdio.h>
04273 #include "midas.h"
04274 void process_event(HNDLE hbuf, HNDLE request_id,
04275            EVENT_HEADER *pheader, void *pevent)
04276 {
04277   printf("Received event #%d\r",
04278   pheader->serial_number);
04279 }
04280 main()
04281 {
04282   INT status, request_id;
04283   HNDLE hbuf;
04284   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
04285   if (status != CM_SUCCESS)
04286   return 1;
04287   bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
04288   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
04289 
04290   do
04291   {
04292    status = cm_yield(1000);
04293   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
04294   cm_disconnect_experiment();
04295   return 0;
04296 }
04297 \endcode
04298 @param buffer_name Name of buffer
04299 @param buffer_size Default size of buffer in bytes. Can by overwritten with ODB value
04300 @param buffer_handle Buffer handle returned by function
04301 @return BM_SUCCESS, BM_CREATED <br>
04302 BM_NO_SHM Shared memory cannot be created <br>
04303 BM_NO_SEMAPHORE Semaphore cannot be created <br>
04304 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
04305 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
04306 different size <br>
04307 BM_INVALID_PARAM Invalid parameter
04308 */
04309 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
04310 {
04311    INT status;
04312 
04313    if (rpc_is_remote()) {
04314       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
04315       bm_mark_read_waiting(TRUE);
04316       return status;
04317    }
04318 #ifdef LOCAL_ROUTINES
04319    {
04320       INT i, handle, size;
04321       BUFFER_CLIENT *pclient;
04322       BOOL shm_created;
04323       HNDLE shm_handle;
04324       BUFFER_HEADER *pheader;
04325       HNDLE hDB, odb_key;
04326       char odb_path[256];
04327       void *p;
04328 
04329       bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
04330 
04331       /* get buffer size from ODB, user parameter as default if not present in ODB */
04332       strlcpy(odb_path, "/Experiment/Buffer sizes/", sizeof(odb_path));
04333       strlcat(odb_path, buffer_name, sizeof(odb_path));
04334 
04335       status = cm_get_experiment_database(&hDB, &odb_key);
04336       assert(status == SUCCESS);
04337       size = sizeof(INT);
04338       status = db_get_value(hDB, 0, odb_path, &buffer_size, &size, TID_DWORD, TRUE);
04339 
04340       if (buffer_size <= 0 || buffer_size > 1 * 1024 * 1024 * 1024) {
04341          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size %d", buffer_size);
04342          return BM_INVALID_PARAM;
04343       }
04344 
04345       if (!buffer_name[0]) {
04346          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
04347          return BM_INVALID_PARAM;
04348       }
04349 
04350       /* allocate new space for the new buffer descriptor */
04351       if (_buffer_entries == 0) {
04352          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
04353          memset(_buffer, 0, sizeof(BUFFER));
04354          if (_buffer == NULL) {
04355             *buffer_handle = 0;
04356             return BM_NO_MEMORY;
04357          }
04358 
04359          _buffer_entries = 1;
04360          i = 0;
04361       } else {
04362          /* check if buffer alreay is open */
04363          for (i = 0; i < _buffer_entries; i++)
04364             if (_buffer[i].attached && equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
04365                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04366                    _buffer[i].index != rpc_get_server_acception())
04367                   continue;
04368 
04369                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i].index != ss_gettid())
04370                   continue;
04371 
04372                *buffer_handle = i + 1;
04373                return BM_SUCCESS;
04374             }
04375 
04376          /* check for a deleted entry */
04377          for (i = 0; i < _buffer_entries; i++)
04378             if (!_buffer[i].attached)
04379                break;
04380 
04381          /* if not found, create new one */
04382          if (i == _buffer_entries) {
04383             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
04384             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
04385 
04386             _buffer_entries++;
04387             if (_buffer == NULL) {
04388                _buffer_entries--;
04389                *buffer_handle = 0;
04390                return BM_NO_MEMORY;
04391             }
04392          }
04393       }
04394 
04395       handle = i;
04396 
04397       if (strlen(buffer_name) >= NAME_LENGTH)
04398          buffer_name[NAME_LENGTH] = 0;
04399 
04400       /* reduce buffer size is larger than maximum */
04401 #ifdef MAX_SHM_SIZE
04402       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
04403          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
04404 #endif
04405 
04406       /* open shared memory region */
04407       //status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04408       //               (void **) &(_buffer[handle].buffer_header), &shm_handle, FALSE);
04409       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_handle, FALSE);
04410       _buffer[handle].buffer_header = (BUFFER_HEADER *) p;
04411 
04412       if (status != SS_SUCCESS && status != SS_CREATED) {
04413          *buffer_handle = 0;
04414          _buffer_entries--;
04415          return BM_NO_SHM;
04416       }
04417 
04418       pheader = _buffer[handle].buffer_header;
04419 
04420       shm_created = (status == SS_CREATED);
04421 
04422       if (shm_created) {
04423          /* setup header info if buffer was created */
04424          memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
04425 
04426          strcpy(pheader->name, buffer_name);
04427          pheader->size = buffer_size;
04428       } else {
04429          /* check if buffer size is identical */
04430          if (pheader->size != buffer_size) {
04431             cm_msg(MERROR, "bm_open_buffer", "Requested buffer size (%d) differs from existing size (%d)",
04432                    buffer_size, pheader->size);
04433             *buffer_handle = 0;
04434             _buffer_entries--;
04435             return BM_MEMSIZE_MISMATCH;
04436          }
04437       }
04438 
04439       /* create semaphore for the buffer */
04440       status = ss_semaphore_create(buffer_name, &(_buffer[handle].semaphore));
04441       if (status != SS_CREATED && status != SS_SUCCESS) {
04442          *buffer_handle = 0;
04443          _buffer_entries--;
04444          return BM_NO_SEMAPHORE;
04445       }
04446 
04447       /* first lock buffer */
04448       bm_lock_buffer(handle + 1);
04449 
04450       /*
04451          Now we have a BUFFER_HEADER, so let's setup a CLIENT
04452          structure in that buffer. The information there can also
04453          be seen by other processes.
04454        */
04455 
04456       for (i = 0; i < MAX_CLIENTS; i++)
04457          if (pheader->client[i].pid == 0)
04458             break;
04459 
04460       if (i == MAX_CLIENTS) {
04461          bm_unlock_buffer(handle + 1);
04462          *buffer_handle = 0;
04463          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
04464          return BM_NO_SLOT;
04465       }
04466 
04467       /* store slot index in _buffer structure */
04468       _buffer[handle].client_index = i;
04469 
04470       /*
04471          Save the index of the last client of that buffer so that later only
04472          the clients 0..max_client_index-1 have to be searched through.
04473        */
04474       pheader->num_clients++;
04475       if (i + 1 > pheader->max_client_index)
04476          pheader->max_client_index = i + 1;
04477 
04478       /* setup buffer header and client structure */
04479       pclient = &pheader->client[i];
04480 
04481       memset(pclient, 0, sizeof(BUFFER_CLIENT));
04482       /* use client name previously set by bm_set_name */
04483       cm_get_client_info(pclient->name);
04484       if (pclient->name[0] == 0)
04485          strcpy(pclient->name, "unknown");
04486       pclient->pid = ss_getpid();
04487 
04488       ss_suspend_get_port(&pclient->port);
04489 
04490       pclient->read_pointer = pheader->write_pointer;
04491       pclient->last_activity = ss_millitime();
04492 
04493       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
04494 
04495       bm_unlock_buffer(handle + 1);
04496 
04497       /* setup _buffer entry */
04498       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
04499       _buffer[handle].attached = TRUE;
04500       _buffer[handle].shm_handle = shm_handle;
04501       _buffer[handle].callback = FALSE;
04502 
04503       /* remember to which connection acutal buffer belongs */
04504       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
04505          _buffer[handle].index = rpc_get_server_acception();
04506       else
04507          _buffer[handle].index = ss_gettid();
04508 
04509       *buffer_handle = (handle + 1);
04510 
04511       /* initialize buffer counters */
04512       bm_init_buffer_counters(handle + 1);
04513 
04514       /* setup dispatcher for receive events */
04515       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
04516 
04517       bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
04518 
04519       if (shm_created)
04520          return BM_CREATED;
04521    }
04522 #endif                          /* LOCAL_ROUTINES */
04523 
04524    return BM_SUCCESS;
04525 }
04526 
04527 /********************************************************************/
04528 /**
04529 Closes an event buffer previously opened with bm_open_buffer().
04530 @param buffer_handle buffer handle
04531 @return BM_SUCCESS, BM_INVALID_HANDLE
04532 */
04533 INT bm_close_buffer(INT buffer_handle)
04534 {
04535    if (rpc_is_remote())
04536       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
04537 
04538 #ifdef LOCAL_ROUTINES
04539    {
04540       BUFFER_CLIENT *pclient;
04541       BUFFER_HEADER *pheader;
04542       INT i, j, idx, destroy_flag;
04543 
04544       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04545          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
04546          return BM_INVALID_HANDLE;
04547       }
04548 
04549       /* check if buffer got already closed */
04550       if (!_buffer[buffer_handle - 1].attached) {
04551          return BM_SUCCESS;
04552       }
04553 
04554       /*
04555          Check if buffer was opened by current thread. This is necessary
04556          in the server process where one thread may not close the buffer
04557          of other threads.
04558        */
04559 
04560       idx = bm_validate_client_index(&_buffer[buffer_handle - 1]);
04561       pheader = _buffer[buffer_handle - 1].buffer_header;
04562 
04563       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04564           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04565          return BM_INVALID_HANDLE;
04566 
04567       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE
04568           && _buffer[buffer_handle - 1].index != ss_gettid())
04569          return BM_INVALID_HANDLE;
04570 
04571       if (!_buffer[buffer_handle - 1].attached) {
04572          /* don't produce error, since bm_close_all_buffers() might want to close an
04573             already closed buffer */
04574          return BM_SUCCESS;
04575       }
04576 
04577       /* delete all requests for this buffer */
04578       for (i = 0; i < _request_list_entries; i++)
04579          if (_request_list[i].buffer_handle == buffer_handle)
04580             bm_delete_request(i);
04581 
04582       /* first lock buffer */
04583       bm_lock_buffer(buffer_handle);
04584 
04585       /* mark entry in _buffer as empty */
04586       _buffer[buffer_handle - 1].attached = FALSE;
04587 
04588       /* clear entry from client structure in buffer header */
04589       memset(&(pheader->client[idx]), 0, sizeof(BUFFER_CLIENT));
04590 
04591       /* calculate new max_client_index entry */
04592       for (i = MAX_CLIENTS - 1; i >= 0; i--)
04593          if (pheader->client[i].pid != 0)
04594             break;
04595       pheader->max_client_index = i + 1;
04596 
04597       /* count new number of clients */
04598       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04599          if (pheader->client[i].pid != 0)
04600             j++;
04601       pheader->num_clients = j;
04602 
04603       destroy_flag = (pheader->num_clients == 0);
04604 
04605       /* free cache */
04606       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04607          M_FREE(_buffer[buffer_handle - 1].read_cache);
04608       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04609          M_FREE(_buffer[buffer_handle - 1].write_cache);
04610 
04611       /* check if anyone is waiting and wake him up */
04612       pclient = pheader->client;
04613 
04614       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04615          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04616             ss_resume(pclient->port, "B  ");
04617 
04618       /* unmap shared memory, delete it if we are the last */
04619       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04620                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04621 
04622       /* unlock buffer */
04623       bm_unlock_buffer(buffer_handle);
04624 
04625       /* delete semaphore */
04626       ss_semaphore_delete(_buffer[buffer_handle - 1].semaphore, destroy_flag);
04627 
04628       /* update _buffer_entries */
04629       if (buffer_handle == _buffer_entries)
04630          _buffer_entries--;
04631 
04632       if (_buffer_entries > 0)
04633          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04634       else {
04635          M_FREE(_buffer);
04636          _buffer = NULL;
04637       }
04638    }
04639 #endif                          /* LOCAL_ROUTINES */
04640 
04641    return BM_SUCCESS;
04642 }
04643 
04644 /********************************************************************/
04645 /**
04646 Close all open buffers
04647 @return BM_SUCCESS
04648 */
04649 INT bm_close_all_buffers(void)
04650 {
04651    if (rpc_is_remote())
04652       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04653 
04654 #ifdef LOCAL_ROUTINES
04655    {
04656       INT i;
04657 
04658       for (i = _buffer_entries; i > 0; i--)
04659          bm_close_buffer(i);
04660    }
04661 #endif                          /* LOCAL_ROUTINES */
04662 
04663    return BM_SUCCESS;
04664 }
04665 
04666 /**dox***************************************************************/
04667                                                                                                                                /** @} *//* end of bmfunctionc */
04668 
04669 /**dox***************************************************************/
04670 /** @addtogroup cmfunctionc
04671  *
04672  *  @{  */
04673 
04674 /*-- Watchdog routines ---------------------------------------------*/
04675 #ifdef LOCAL_ROUTINES
04676 
04677 /********************************************************************/
04678 /**
04679 Called at periodic intervals, checks if all clients are
04680 alive. If one process died, its client entries are cleaned up.
04681 @param dummy unused!
04682 */
04683 void cm_watchdog(int dummy)
04684 {
04685    DATABASE_HEADER *pdbheader;
04686    DATABASE_CLIENT *pdbclient;
04687    KEY *pkey;
04688    DWORD actual_time, interval;
04689    INT client_pid;
04690    INT i, j, k, nc, status;
04691    BOOL bDeleted, time_changed, wrong_interval;
04692    char str[256];
04693 
04694    i = dummy;                   /* avoid compiler warning */
04695 
04696    /* return immediately if watchdog has been disabled in meantime */
04697    if (!_call_watchdog)
04698       return;
04699 
04700    /* tell system services that we are in async mode ... */
04701    ss_set_async_flag(TRUE);
04702 
04703    /* Calculate the time since last watchdog call. Kill clients if they
04704       are inactive for more than the timeout they specified */
04705    actual_time = ss_millitime();
04706    if (_watchdog_last_called == 0)
04707       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04708    interval = actual_time - _watchdog_last_called;
04709 
04710    /* check if system time has been changed more than 10 min */
04711    time_changed = interval > 600000;
04712    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL || interval > 1.2 * WATCHDOG_INTERVAL;
04713 
04714    if (time_changed)
04715       cm_msg(MINFO, "cm_watchdog",
04716              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04717              _watchdog_last_called, actual_time, interval);
04718 
04719    bm_cleanup("cm_watchdog", actual_time, wrong_interval);
04720 
04721    /* check online databases */
04722    for (i = 0; i < _database_entries; i++)
04723       if (_database[i].attached) {
04724          /* update the last_activity entry to show that we are alive */
04725          pdbheader = _database[i].database_header;
04726          pdbclient = pdbheader->client;
04727          pdbclient[_database[i].client_index].last_activity = actual_time;
04728 
04729          /* don't check other clients if interval is stange */
04730          if (wrong_interval)
04731             continue;
04732 
04733          /* now check other clients */
04734          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04735             /* If client process has no activity, clear its buffer entry. */
04736             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04737                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04738                client_pid = pdbclient->pid;
04739                bDeleted = FALSE;
04740                db_lock_database(i + 1);
04741                str[0] = 0;
04742 
04743                /* now make again the check with the buffer locked */
04744                actual_time = ss_millitime();
04745                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04746                    actual_time > pdbclient->last_activity &&
04747                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04748                   sprintf(str,
04749                           "Client \'%s\' (PID %d) on buffer \'%s\' removed by cm_watchdog (idle %1.1lfs,TO %1.0lfs)",
04750                           pdbclient->name, client_pid, pdbheader->name,
04751                           (actual_time - pdbclient->last_activity) / 1000.0,
04752                           pdbclient->watchdog_timeout / 1000.0);
04753 
04754                   /* decrement notify_count for open records and clear exclusive mode */
04755                   for (k = 0; k < pdbclient->max_index; k++)
04756                      if (pdbclient->open_record[k].handle) {
04757                         pkey = (KEY *) ((char *) pdbheader + pdbclient->open_record[k].handle);
04758                         if (pkey->notify_count > 0)
04759                            pkey->notify_count--;
04760 
04761                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04762                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04763                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04764                      }
04765 
04766                   /* clear entry from client structure in buffer header */
04767                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04768 
04769                   /* calculate new max_client_index entry */
04770                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04771                      if (pdbheader->client[k].pid != 0)
04772                         break;
04773                   pdbheader->max_client_index = k + 1;
04774 
04775                   /* count new number of clients */
04776                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04777                      if (pdbheader->client[k].pid != 0)
04778                         nc++;
04779                   pdbheader->num_clients = nc;
04780                   bDeleted = TRUE;
04781                }
04782 
04783                /* delete client entry before unlocking db */
04784                if (bDeleted) {
04785                   status = cm_delete_client_info(i + 1, client_pid);
04786                   if (status != CM_SUCCESS)
04787                      cm_msg(MERROR, "cm_watchdog",
04788                             "cannot delete client info for client \'%s\', pid %d from buffer \'%s\', status %d",
04789                             pdbclient->name, client_pid, pdbheader->name, status);
04790                }
04791 
04792                db_unlock_database(i + 1);
04793 
04794                /* display info message after unlocking db */
04795                if (str[0])
04796                   cm_msg(MINFO, "cm_watchdog", str);
04797             }
04798       }
04799 
04800    _watchdog_last_called = actual_time;
04801 
04802    ss_set_async_flag(FALSE);
04803 
04804    /* Schedule next watchdog call */
04805    if (_call_watchdog)
04806       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04807 }
04808 
04809 /********************************************************************/
04810 /**
04811 Temporarily disable watchdog calling. Used for tape IO
04812 not to interrupt lengthy operations like mount.
04813 @param flag FALSE for disable, TRUE for re-enable
04814 @return CM_SUCCESS
04815 */
04816 INT cm_enable_watchdog(BOOL flag)
04817 {
04818    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
04819    static BOOL call_flag = FALSE;
04820 
04821    if (flag) {
04822       if (call_flag)
04823          cm_set_watchdog_params(TRUE, timeout);
04824    } else {
04825       call_flag = _call_watchdog;
04826       timeout = _watchdog_timeout;
04827       if (call_flag)
04828          cm_set_watchdog_params(FALSE, 0);
04829    }
04830 
04831    return CM_SUCCESS;
04832 }
04833 
04834 #endif                          /* local routines */
04835 
04836 /********************************************************************/
04837 /**
04838 Shutdown (exit) other MIDAS client
04839 @param name           Client name or "all" for all clients
04840 @param bUnique        If true, look for the exact client name.
04841                       If false, look for namexxx where xxx is
04842                       a any number.
04843 
04844 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY
04845 */
04846 INT cm_shutdown(const char *name, BOOL bUnique)
04847 {
04848    INT status, return_status, i, size;
04849    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
04850    KEY key;
04851    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
04852    INT port;
04853    DWORD start_time;
04854 
04855    cm_get_experiment_database(&hDB, &hKeyClient);
04856 
04857    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04858    if (status != DB_SUCCESS)
04859       return DB_NO_KEY;
04860 
04861    return_status = CM_NO_CLIENT;
04862 
04863    /* loop over all clients */
04864    for (i = 0;; i++) {
04865       status = db_enum_key(hDB, hKey, i, &hSubkey);
04866       if (status == DB_NO_MORE_SUBKEYS)
04867          break;
04868 
04869       /* don't shutdown ourselves */
04870       if (hSubkey == hKeyClient)
04871          continue;
04872 
04873       if (status == DB_SUCCESS) {
04874          db_get_key(hDB, hSubkey, &key);
04875 
04876          /* contact client */
04877          size = sizeof(client_name);
04878          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04879 
04880          if (!bUnique)
04881             client_name[strlen(name)] = 0;      /* strip number */
04882 
04883          /* check if individual client */
04884          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
04885             continue;
04886 
04887          size = sizeof(port);
04888          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
04889 
04890          size = sizeof(remote_host);
04891          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
04892 
04893          /* client found -> connect to its server port */
04894          status = rpc_client_connect(remote_host, port, client_name, &hConn);
04895          if (status != RPC_SUCCESS) {
04896             int client_pid = atoi(key.name);
04897             return_status = CM_NO_CLIENT;
04898             cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
04899                    client_name, remote_host, port);
04900 #ifdef SIGKILL
04901             cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
04902                    client_pid);
04903             kill(client_pid, SIGKILL);
04904             status = cm_delete_client_info(hDB, client_pid);
04905             if (status != CM_SUCCESS)
04906                cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
04907                       name, client_pid, status);
04908 #endif
04909          } else {
04910             /* call disconnect with shutdown=TRUE */
04911             rpc_client_disconnect(hConn, TRUE);
04912 
04913             /* wait until client has shut down */
04914             start_time = ss_millitime();
04915             do {
04916                ss_sleep(100);
04917                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
04918             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
04919 
04920             if (status == DB_SUCCESS) {
04921                int client_pid = atoi(key.name);
04922                return_status = CM_NO_CLIENT;
04923                cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
04924 #ifdef SIGKILL
04925                cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
04926                       client_pid);
04927                kill(client_pid, SIGKILL);
04928                status = cm_delete_client_info(hDB, client_pid);
04929                if (status != CM_SUCCESS)
04930                   cm_msg(MERROR, "cm_shutdown",
04931                          "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
04932                          status);
04933 #endif
04934                return_status = CM_NO_CLIENT;
04935             } else {
04936                return_status = CM_SUCCESS;
04937                i--;
04938             }
04939          }
04940       }
04941    }
04942 
04943    return return_status;
04944 }
04945 
04946 /********************************************************************/
04947 /**
04948 Check if a MIDAS client exists in current experiment
04949 @param    name            Client name
04950 @param    bUnique         If true, look for the exact client name.
04951                           If false, look for namexxx where xxx is
04952                           a any number
04953 @return   CM_SUCCESS, CM_NO_CLIENT
04954 */
04955 INT cm_exist(const char *name, BOOL bUnique)
04956 {
04957    INT status, i, size;
04958    HNDLE hDB, hKeyClient, hKey, hSubkey;
04959    char client_name[NAME_LENGTH];
04960 
04961    if (rpc_is_remote())
04962       return rpc_call(RPC_CM_EXIST, name, bUnique);
04963 
04964    cm_get_experiment_database(&hDB, &hKeyClient);
04965 
04966    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04967    if (status != DB_SUCCESS)
04968       return DB_NO_KEY;
04969 
04970    /* loop over all clients */
04971    for (i = 0;; i++) {
04972       status = db_enum_key(hDB, hKey, i, &hSubkey);
04973       if (status == DB_NO_MORE_SUBKEYS)
04974          break;
04975 
04976       if (hSubkey == hKeyClient)
04977          continue;
04978 
04979       if (status == DB_SUCCESS) {
04980          /* get client name */
04981          size = sizeof(client_name);
04982          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04983 
04984          if (equal_ustring(client_name, name))
04985             return CM_SUCCESS;
04986 
04987          if (!bUnique) {
04988             client_name[strlen(name)] = 0;      /* strip number */
04989             if (equal_ustring(client_name, name))
04990                return CM_SUCCESS;
04991          }
04992       }
04993    }
04994 
04995    return CM_NO_CLIENT;
04996 }
04997 
04998 /********************************************************************/
04999 /**
05000 Remove hanging clients independent of their watchdog
05001            timeout.
05002 
05003 Since this function does not obey the client watchdog
05004 timeout, it should be only called to remove clients which
05005 have their watchdog checking turned off or which are
05006 known to be dead. The normal client removement is done
05007 via cm_watchdog().
05008 
05009 Currently (Sept. 02) there are two applications for that:
05010 -# The ODBEdit command "cleanup", which can be used to
05011 remove clients which have their watchdog checking off,
05012 like the analyzer started with the "-d" flag for a
05013 debugging session.
05014 -# The frontend init code to remove previous frontends.
05015 This can be helpful if a frontend dies. Normally,
05016 one would have to wait 60 sec. for a crashed frontend
05017 to be removed. Only then one can start again the
05018 frontend. Since the frontend init code contains a
05019 call to cm_cleanup(<frontend_name>), one can restart
05020 a frontend immediately.
05021 
05022 Added ignore_timeout on Nov.03. A logger might have an
05023 increased tiemout of up to 60 sec. because of tape
05024 operations. If ignore_timeout is FALSE, the logger is
05025 then not killed if its inactivity is less than 60 sec.,
05026 while in the previous implementation it was always
05027 killed after 2*WATCHDOG_INTERVAL.
05028 @param    client_name      Client name, if zero check all clients
05029 @param    ignore_timeout   If TRUE, ignore a possible increased
05030                            timeout defined by each client.
05031 @return   CM_SUCCESS
05032 */
05033 INT cm_cleanup(const char *client_name, BOOL ignore_timeout)
05034 {
05035    if (rpc_is_remote())
05036       return rpc_call(RPC_CM_CLEANUP, client_name);
05037 
05038 #ifdef LOCAL_ROUTINES
05039    {
05040       BUFFER_HEADER *pheader = NULL;
05041       BUFFER_CLIENT *pbclient;
05042       DATABASE_HEADER *pdbheader;
05043       DATABASE_CLIENT *pdbclient;
05044       KEY *pkey;
05045       INT client_pid;
05046       INT i, j, k, status, nc;
05047       BOOL bDeleted;
05048       char str[256];
05049       DWORD interval;
05050       DWORD now = ss_millitime();
05051 
05052       /* check buffers */
05053       for (i = 0; i < _buffer_entries; i++)
05054          if (_buffer[i].attached) {
05055             /* update the last_activity entry to show that we are alive */
05056             pheader = _buffer[i].buffer_header;
05057             pbclient = pheader->client;
05058             pbclient[bm_validate_client_index(&_buffer[i])].last_activity = ss_millitime();
05059 
05060             /* now check other clients */
05061             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
05062                if (j != _buffer[i].client_index && pbclient->pid &&
05063                    (client_name == NULL || client_name[0] == 0
05064                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
05065                   if (ignore_timeout)
05066                      interval = 2 * WATCHDOG_INTERVAL;
05067                   else
05068                      interval = pbclient->watchdog_timeout;
05069 
05070                   /* If client process has no activity, clear its buffer entry. */
05071                   if (interval > 0
05072                       && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
05073                      bm_lock_buffer(i + 1);
05074                      str[0] = 0;
05075 
05076                      /* now make again the check with the buffer locked */
05077                      if (interval > 0
05078                          && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
05079                         sprintf(str,
05080                                 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs,TO %1.0lfs)",
05081                                 pbclient->name, pheader->name,
05082                                 (ss_millitime() - pbclient->last_activity) / 1000.0, interval / 1000.0);
05083 
05084                         bm_remove_client_locked(pheader, j);
05085                      }
05086 
05087                      bm_unlock_buffer(i + 1);
05088 
05089                      /* display info message after unlocking buffer */
05090                      if (str[0])
05091                         cm_msg(MINFO, "cm_cleanup", str);
05092 
05093                      /* go again through whole list */
05094                      j = 0;
05095                   }
05096                }
05097          }
05098 
05099       /* check online databases */
05100       for (i = 0; i < _database_entries; i++)
05101          if (_database[i].attached) {
05102             /* update the last_activity entry to show that we are alive */
05103             db_lock_database(i + 1);
05104 
05105             pdbheader = _database[i].database_header;
05106             pdbclient = pdbheader->client;
05107             pdbclient[_database[i].client_index].last_activity = ss_millitime();
05108 
05109             /* now check other clients */
05110             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
05111                if (j != _database[i].client_index && pdbclient->pid &&
05112                    (client_name == NULL || client_name[0] == 0
05113                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
05114                   client_pid = pdbclient->pid;
05115                   if (ignore_timeout)
05116                      interval = 2 * WATCHDOG_INTERVAL;
05117                   else
05118                      interval = pdbclient->watchdog_timeout;
05119 
05120                   /* If client process has no activity, clear its buffer entry. */
05121 
05122                   if (interval > 0 && ss_millitime() - pdbclient->last_activity > interval) {
05123                      bDeleted = FALSE;
05124                      str[0] = 0;
05125 
05126                      /* now make again the check with the buffer locked */
05127                      if (interval > 0 && ss_millitime() - pdbclient->last_activity > interval) {
05128                         sprintf(str,
05129                                 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs,TO %1.0lfs)",
05130                                 pdbclient->name, pdbheader->name,
05131                                 (ss_millitime() - pdbclient->last_activity) / 1000.0, interval / 1000.0);
05132 
05133                         /* decrement notify_count for open records and clear exclusive mode */
05134                         for (k = 0; k < pdbclient->max_index; k++)
05135                            if (pdbclient->open_record[k].handle) {
05136                               pkey = (KEY *) ((char *) pdbheader + pdbclient->open_record[k].handle);
05137                               if (pkey->notify_count > 0)
05138                                  pkey->notify_count--;
05139 
05140                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
05141                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
05142                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
05143                            }
05144 
05145                         /* clear entry from client structure in buffer header */
05146                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
05147 
05148                         /* calculate new max_client_index entry */
05149                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
05150                            if (pdbheader->client[k].pid != 0)
05151                               break;
05152                         pdbheader->max_client_index = k + 1;
05153 
05154                         /* count new number of clients */
05155                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05156                            if (pheader->client[k].pid != 0)
05157                               nc++;
05158                         pdbheader->num_clients = nc;
05159 
05160                         bDeleted = TRUE;
05161                      }
05162 
05163 
05164                      /* delete client entry after unlocking db */
05165                      if (bDeleted) {
05166                         db_unlock_database(i + 1);
05167 
05168                         /* display info message after unlocking buffer */
05169                         cm_msg(MINFO, "cm_cleanup", str);
05170 
05171                         status = cm_delete_client_info(i + 1, client_pid);
05172                         if (status != CM_SUCCESS)
05173                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
05174 
05175                         /* re-lock database */
05176                         db_lock_database(i + 1);
05177                         pdbheader = _database[i].database_header;
05178                         pdbclient = pdbheader->client;
05179 
05180                         /* go again though whole list */
05181                         j = 0;
05182                      }
05183                   }
05184                }
05185 
05186             db_unlock_database(i + 1);
05187          }
05188 
05189    }
05190 #endif                          /* LOCAL_ROUTINES */
05191 
05192    return CM_SUCCESS;
05193 }
05194 
05195 /**dox***************************************************************/
05196 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05197 
05198 /********************************************************************/
05199 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
05200 /********************************************************************\
05201 
05202   Routine: bm_buffer_info
05203 
05204   Purpose: Copies the current buffer header referenced by buffer_handle
05205            into the *buffer_header structure which must be supplied
05206            by the calling routine.
05207 
05208   Input:
05209     INT buffer_handle       Handle of the buffer to get the header from
05210 
05211   Output:
05212     BUFFER_HEADER *buffer_header   Destination address which gets a copy
05213                                    of the buffer header structure.
05214 
05215   Function value:
05216     BM_SUCCESS              Successful completion
05217     BM_INVALID_HANDLE       Buffer handle is invalid
05218     RPC_NET_ERROR           Network error
05219 
05220 \********************************************************************/
05221 {
05222    if (rpc_is_remote())
05223       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
05224 
05225 #ifdef LOCAL_ROUTINES
05226 
05227    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05228       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05229       return BM_INVALID_HANDLE;
05230    }
05231 
05232    if (!_buffer[buffer_handle - 1].attached) {
05233       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05234       return BM_INVALID_HANDLE;
05235    }
05236 
05237    bm_lock_buffer(buffer_handle);
05238 
05239    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
05240 
05241    bm_unlock_buffer(buffer_handle);
05242 
05243 #endif                          /* LOCAL_ROUTINES */
05244 
05245    return BM_SUCCESS;
05246 }
05247 
05248 /********************************************************************/
05249 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
05250 /********************************************************************\
05251 
05252   Routine: bm_get_buffer_level
05253 
05254   Purpose: Return number of bytes in buffer or in cache
05255 
05256   Input:
05257     INT buffer_handle       Handle of the buffer to get the info
05258 
05259   Output:
05260     INT *n_bytes              Number of bytes in buffer
05261 
05262   Function value:
05263     BM_SUCCESS              Successful completion
05264     BM_INVALID_HANDLE       Buffer handle is invalid
05265     RPC_NET_ERROR           Network error
05266 
05267 \********************************************************************/
05268 {
05269    if (rpc_is_remote())
05270       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
05271 
05272 #ifdef LOCAL_ROUTINES
05273    {
05274       BUFFER *pbuf;
05275       BUFFER_HEADER *pheader;
05276       BUFFER_CLIENT *pclient;
05277 
05278       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05279          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05280          return BM_INVALID_HANDLE;
05281       }
05282 
05283       pbuf = &_buffer[buffer_handle - 1];
05284       pheader = pbuf->buffer_header;
05285 
05286       if (!pbuf->attached) {
05287          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05288          return BM_INVALID_HANDLE;
05289       }
05290 
05291       bm_lock_buffer(buffer_handle);
05292 
05293       pclient = &(pheader->client[bm_validate_client_index(pbuf)]);
05294 
05295       *n_bytes = pheader->write_pointer - pclient->read_pointer;
05296       if (*n_bytes < 0)
05297          *n_bytes += pheader->size;
05298 
05299       bm_unlock_buffer(buffer_handle);
05300 
05301       /* add bytes in cache */
05302       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
05303          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
05304    }
05305 #endif                          /* LOCAL_ROUTINES */
05306 
05307    return BM_SUCCESS;
05308 }
05309 
05310 
05311 
05312 #ifdef LOCAL_ROUTINES
05313 
05314 /********************************************************************/
05315 INT bm_lock_buffer(INT buffer_handle)
05316 /********************************************************************\
05317 
05318   Routine: bm_lock_buffer
05319 
05320   Purpose: Lock a buffer for exclusive access via system semaphore calls.
05321 
05322   Input:
05323     INT    bufer_handle     Handle to the buffer to lock
05324   Output:
05325     none
05326 
05327   Function value:
05328     BM_SUCCESS              Successful completion
05329     BM_INVALID_HANDLE       Buffer handle is invalid
05330 
05331 \********************************************************************/
05332 {
05333    int status;
05334 
05335    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05336       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
05337       return BM_INVALID_HANDLE;
05338    }
05339 
05340    status = ss_semaphore_wait_for(_buffer[buffer_handle - 1].semaphore, 5 * 60 * 1000);
05341 
05342    if (status != SS_SUCCESS) {
05343       cm_msg(MERROR, "bm_lock_buffer",
05344              "Cannot lock buffer handle %d, ss_semaphore_wait_for() status %d", buffer_handle, status);
05345       abort();
05346       return BM_INVALID_HANDLE;
05347    }
05348 
05349    return BM_SUCCESS;
05350 }
05351 
05352 /********************************************************************/
05353 INT bm_unlock_buffer(INT buffer_handle)
05354 /********************************************************************\
05355 
05356   Routine: bm_unlock_buffer
05357 
05358   Purpose: Unlock a buffer via system semaphore calls.
05359 
05360   Input:
05361     INT    bufer_handle     Handle to the buffer to lock
05362   Output:
05363     none
05364 
05365   Function value:
05366     BM_SUCCESS              Successful completion
05367     BM_INVALID_HANDLE       Buffer handle is invalid
05368 
05369 \********************************************************************/
05370 {
05371    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05372       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
05373       return BM_INVALID_HANDLE;
05374    }
05375 
05376    ss_semaphore_release(_buffer[buffer_handle - 1].semaphore);
05377    return BM_SUCCESS;
05378 }
05379 
05380 #endif                          /* LOCAL_ROUTINES */
05381 
05382 /********************************************************************/
05383 INT bm_init_buffer_counters(INT buffer_handle)
05384 /********************************************************************\
05385 
05386   Routine: bm_init_event_counters
05387 
05388   Purpose: Initialize counters for a specific buffer. This routine
05389            should be called at the beginning of a run.
05390 
05391   Input:
05392     INT    buffer_handle    Handle to the buffer to be
05393                             initialized.
05394   Output:
05395     none
05396 
05397   Function value:
05398     BM_SUCCESS              Successful completion
05399     BM_INVALID_HANDLE       Buffer handle is invalid
05400 
05401 \********************************************************************/
05402 {
05403    if (rpc_is_remote())
05404       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
05405 
05406 #ifdef LOCAL_ROUTINES
05407 
05408    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05409       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d", buffer_handle);
05410       return BM_INVALID_HANDLE;
05411    }
05412 
05413    if (!_buffer[buffer_handle - 1].attached) {
05414       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d", buffer_handle);
05415       return BM_INVALID_HANDLE;
05416    }
05417 
05418    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
05419    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
05420 
05421 #endif                          /* LOCAL_ROUTINES */
05422 
05423    return BM_SUCCESS;
05424 }
05425 
05426 /**dox***************************************************************/
05427 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05428 
05429 /**dox***************************************************************/
05430                                                                                                                                /** @} *//* end of cmfunctionc */
05431 
05432 /**dox***************************************************************/
05433 /** @addtogroup bmfunctionc
05434  *
05435  *  @{  */
05436 
05437 /********************************************************************/
05438 /**
05439 Modifies buffer cache size.
05440 Without a buffer cache, events are copied to/from the shared
05441 memory event by event.
05442 
05443 To protect processed from accessing the shared memory simultaneously,
05444 semaphores are used. Since semaphore operations are CPU consuming (typically
05445 50-100us) this can slow down the data transfer especially for small events.
05446 By using a cache the number of semaphore operations is reduced dramatically.
05447 Instead writing directly to the shared memory, the events are copied to a
05448 local cache buffer. When this buffer is full, it is copied to the shared
05449 memory in one operation. The same technique can be used when receiving events.
05450 
05451 The drawback of this method is that the events have to be copied twice, once to the
05452 cache and once from the cache to the shared memory. Therefore it can happen that the
05453 usage of a cache even slows down data throughput on a given environment (computer
05454 type, OS type, event size).
05455 The cache size has therefore be optimized manually to maximize data throughput.
05456 @param buffer_handle buffer handle obtained via bm_open_buffer()
05457 @param read_size cache size for reading events in bytes, zero for no cache
05458 @param write_size cache size for writing events in bytes, zero for no cache
05459 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
05460 */
05461 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
05462 /*------------------------------------------------------------------*/
05463 {
05464    if (rpc_is_remote())
05465       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
05466 
05467 #ifdef LOCAL_ROUTINES
05468    {
05469       BUFFER *pbuf;
05470 
05471       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05472          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05473          return BM_INVALID_HANDLE;
05474       }
05475 
05476       if (!_buffer[buffer_handle - 1].attached) {
05477          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05478          return BM_INVALID_HANDLE;
05479       }
05480 
05481       if (read_size < 0 || read_size > 1E6) {
05482          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
05483          return BM_INVALID_PARAM;
05484       }
05485 
05486       if (write_size < 0 || write_size > 1E6) {
05487          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
05488          return BM_INVALID_PARAM;
05489       }
05490 
05491       /* manage read cache */
05492       pbuf = &_buffer[buffer_handle - 1];
05493 
05494       if (pbuf->read_cache_size > 0)
05495          M_FREE(pbuf->read_cache);
05496 
05497       if (read_size > 0) {
05498          pbuf->read_cache = (char *) M_MALLOC(read_size);
05499          if (pbuf->read_cache == NULL) {
05500             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate cache buffer");
05501             return BM_NO_MEMORY;
05502          }
05503       }
05504 
05505       pbuf->read_cache_size = read_size;
05506       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05507 
05508       /* manage write cache */
05509       if (pbuf->write_cache_size > 0)
05510          M_FREE(pbuf->write_cache);
05511 
05512       if (write_size > 0) {
05513          pbuf->write_cache = (char *) M_MALLOC(write_size);
05514          if (pbuf->write_cache == NULL) {
05515             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate cache buffer");
05516             return BM_NO_MEMORY;
05517          }
05518       }
05519 
05520       pbuf->write_cache_size = write_size;
05521       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05522 
05523    }
05524 #endif                          /* LOCAL_ROUTINES */
05525 
05526    return BM_SUCCESS;
05527 }
05528 
05529 /********************************************************************/
05530 /**
05531 Compose a Midas event header.
05532 An event header can usually be set-up manually or
05533 through this routine. If the data size of the event is not known when
05534 the header is composed, it can be set later with event_header->data-size = <...>
05535 Following structure is created at the beginning of an event
05536 \code
05537 typedef struct {
05538  short int     event_id;
05539  short int     trigger_mask;
05540  DWORD         serial_number;
05541  DWORD         time_stamp;
05542  DWORD         data_size;
05543 } EVENT_HEADER;
05544 
05545 char event[1000];
05546  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05547  *(event+sizeof(EVENT_HEADER)) = <...>
05548 \endcode
05549 @param event_header pointer to the event header
05550 @param event_id event ID of the event
05551 @param trigger_mask trigger mask of the event
05552 @param size size if the data part of the event in bytes
05553 @param serial serial number
05554 @return BM_SUCCESS
05555 */
05556 INT bm_compose_event(EVENT_HEADER * event_header, short int event_id, short int trigger_mask, DWORD size,
05557                      DWORD serial)
05558 {
05559    event_header->event_id = event_id;
05560    event_header->trigger_mask = trigger_mask;
05561    event_header->data_size = size;
05562    event_header->time_stamp = ss_time();
05563    event_header->serial_number = serial;
05564 
05565    return BM_SUCCESS;
05566 }
05567 
05568 
05569 /**dox***************************************************************/
05570 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05571 
05572 /********************************************************************/
05573 INT bm_add_event_request(INT buffer_handle, short int event_id,
05574                          short int trigger_mask,
05575                          INT sampling_type, void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *),
05576                          INT request_id)
05577 /********************************************************************\
05578 
05579   Routine:  bm_add_event_request
05580 
05581   Purpose:  Place a request for a specific event type in the client
05582             structure of the buffer refereced by buffer_handle.
05583 
05584   Input:
05585     INT          buffer_handle  Handle to the buffer where the re-
05586                                 quest should be placed in
05587 
05588     short int    event_id       Event ID      \
05589     short int    trigger_mask   Trigger mask  / Event specification
05590 
05591     INT          sampling_type  One of GET_ALL, GET_NONBLOCKING or GET_RECENT
05592 
05593 
05594                  Note: to request all types of events, use
05595                    event_id = 0 (all others should be !=0 !)
05596                    trigger_mask = TRIGGER_ALL
05597                    sampling_typ = GET_ALL
05598 
05599 
05600     void         *func          Callback function
05601     INT          request_id     Request id (unique number assigned
05602                                 by bm_request_event)
05603 
05604   Output:
05605     none
05606 
05607   Function value:
05608     BM_SUCCESS              Successful completion
05609     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05610                             MIDAS.H should be increased.
05611     BM_INVALID_HANDLE       Buffer handle is invalid
05612     BM_INVALID_PARAM        GET_RECENT is used with non-zero cache size
05613     RPC_NET_ERROR           Network error
05614 
05615 \********************************************************************/
05616 {
05617    if (rpc_is_remote())
05618       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05619                       trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
05620 
05621 #ifdef LOCAL_ROUTINES
05622    {
05623       INT i;
05624       BUFFER_CLIENT *pclient;
05625 
05626       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05627          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d", buffer_handle);
05628          return BM_INVALID_HANDLE;
05629       }
05630 
05631       if (!_buffer[buffer_handle - 1].attached) {
05632          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d", buffer_handle);
05633          return BM_INVALID_HANDLE;
05634       }
05635 
05636       /* avoid callback/non callback requests */
05637       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05638          cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
05639          return BM_INVALID_MIXING;
05640       }
05641 
05642       /* do not allow GET_RECENT with nonzero cache size */
05643       if (sampling_type == GET_RECENT && _buffer[buffer_handle - 1].read_cache_size > 0)
05644          return BM_INVALID_PARAM;
05645 
05646       /* get a pointer to the proper client structure */
05647       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05648                   client[bm_validate_client_index(&_buffer[buffer_handle - 1])]);
05649 
05650       /* lock buffer */
05651       bm_lock_buffer(buffer_handle);
05652 
05653       /* look for a empty request entry */
05654       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05655          if (!pclient->event_request[i].valid)
05656             break;
05657 
05658       if (i == MAX_EVENT_REQUESTS) {
05659          bm_unlock_buffer(buffer_handle);
05660          return BM_NO_MEMORY;
05661       }
05662 
05663       /* setup event_request structure */
05664       pclient->event_request[i].id = request_id;
05665       pclient->event_request[i].valid = TRUE;
05666       pclient->event_request[i].event_id = event_id;
05667       pclient->event_request[i].trigger_mask = trigger_mask;
05668       pclient->event_request[i].sampling_type = sampling_type;
05669 
05670       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05671 
05672       /* set callback flag in buffer structure */
05673       if (func != NULL)
05674          _buffer[buffer_handle - 1].callback = TRUE;
05675 
05676       /*
05677          Save the index of the last request in the list so that later only the
05678          requests 0..max_request_index-1 have to be searched through.
05679        */
05680 
05681       if (i + 1 > pclient->max_request_index)
05682          pclient->max_request_index = i + 1;
05683 
05684       bm_unlock_buffer(buffer_handle);
05685    }
05686 #endif                          /* LOCAL_ROUTINES */
05687 
05688    return BM_SUCCESS;
05689 }
05690 
05691 /**dox***************************************************************/
05692 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05693 
05694 /********************************************************************/
05695 /**
05696 Place an event request based on certain characteristics.
05697 Multiple event requests can be placed for each buffer, which
05698 are later identified by their request ID. They can contain different callback
05699 routines. Example see bm_open_buffer() and bm_receive_event()
05700 @param buffer_handle buffer handle obtained via bm_open_buffer()
05701 @param event_id event ID for requested events. Use EVENTID_ALL
05702 to receive events with any ID.
05703 @param trigger_mask trigger mask for requested events.
05704 The requested events must have at least one bit in its
05705 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05706 receive events with any trigger mask.
05707 @param sampling_type specifies how many events to receive.
05708 A value of GET_ALL receives all events which
05709 match the specified event ID and trigger mask. If the events are consumed slower
05710 than produced, the producer is automatically slowed down. A value of GET_NONBLOCKING
05711 receives as much events as possible without slowing down the producer. GET_ALL is
05712 typically used by the logger, while GET_NONBLOCKING is typically used by analyzers.
05713 @param request_id request ID returned by the function.
05714 This ID is passed to the callback routine and must
05715 be used in the bm_delete_request() routine.
05716 @param func allback routine which gets called when an event of the
05717 specified type is received.
05718 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05719 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05720 should be increased.
05721 */
05722 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05723                      short int trigger_mask,
05724                      INT sampling_type, HNDLE * request_id, void (*func) (HNDLE, HNDLE, EVENT_HEADER *,
05725                                                                           void *))
05726 {
05727    INT idx, status;
05728 
05729    /* allocate new space for the local request list */
05730    if (_request_list_entries == 0) {
05731       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05732       memset(_request_list, 0, sizeof(REQUEST_LIST));
05733       if (_request_list == NULL) {
05734          cm_msg(MERROR, "bm_request_event", "not enough memory to allocate request list buffer");
05735          return BM_NO_MEMORY;
05736       }
05737 
05738       _request_list_entries = 1;
05739       idx = 0;
05740    } else {
05741       /* check for a deleted entry */
05742       for (idx = 0; idx < _request_list_entries; idx++)
05743          if (!_request_list[idx].buffer_handle)
05744             break;
05745 
05746       /* if not found, create new one */
05747       if (idx == _request_list_entries) {
05748          _request_list =
05749              (REQUEST_LIST *) realloc(_request_list, sizeof(REQUEST_LIST) * (_request_list_entries + 1));
05750          if (_request_list == NULL) {
05751             cm_msg(MERROR, "bm_request_event", "not enough memory to allocate request list buffer");
05752             return BM_NO_MEMORY;
05753          }
05754 
05755          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05756 
05757          _request_list_entries++;
05758       }
05759    }
05760 
05761    /* initialize request list */
05762    _request_list[idx].buffer_handle = buffer_handle;
05763    _request_list[idx].event_id = event_id;
05764    _request_list[idx].trigger_mask = trigger_mask;
05765    _request_list[idx].dispatcher = func;
05766 
05767    *request_id = idx;
05768 
05769    /* add request in buffer structure */
05770    status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, idx);
05771    if (status != BM_SUCCESS)
05772       return status;
05773 
05774    return BM_SUCCESS;
05775 }
05776 
05777 /********************************************************************/
05778 /**
05779 Delete a previously placed request for a specific event
05780 type in the client structure of the buffer refereced by buffer_handle.
05781 @param buffer_handle  Handle to the buffer where the re-
05782                                 quest should be placed in
05783 @param request_id     Request id returned by bm_request_event
05784 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR
05785 */
05786 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05787 {
05788    if (rpc_is_remote())
05789       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
05790 
05791 #ifdef LOCAL_ROUTINES
05792    {
05793       INT i, deleted;
05794       BUFFER_CLIENT *pclient;
05795 
05796       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05797          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d", buffer_handle);
05798          return BM_INVALID_HANDLE;
05799       }
05800 
05801       if (!_buffer[buffer_handle - 1].attached) {
05802          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d", buffer_handle);
05803          return BM_INVALID_HANDLE;
05804       }
05805 
05806       /* get a pointer to the proper client structure */
05807       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05808                   client[bm_validate_client_index(&_buffer[buffer_handle - 1])]);
05809 
05810       /* lock buffer */
05811       bm_lock_buffer(buffer_handle);
05812 
05813       /* check all requests and set to zero if matching */
05814       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
05815          if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
05816             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
05817             deleted++;
05818          }
05819 
05820       /* calculate new max_request_index entry */
05821       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
05822          if (pclient->event_request[i].valid)
05823             break;
05824 
05825       pclient->max_request_index = i + 1;
05826 
05827       /* caluclate new all_flag */
05828       pclient->all_flag = FALSE;
05829 
05830       for (i = 0; i < pclient->max_request_index; i++)
05831          if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
05832             pclient->all_flag = TRUE;
05833             break;
05834          }
05835 
05836       bm_unlock_buffer(buffer_handle);
05837 
05838       if (!deleted)
05839          return BM_NOT_FOUND;
05840    }
05841 #endif                          /* LOCAL_ROUTINES */
05842 
05843    return BM_SUCCESS;
05844 }
05845 
05846 /********************************************************************/
05847 /**
05848 Deletes an event request previously done with bm_request_event().
05849 When an event request gets deleted, events of that requested type are
05850 not received any more. When a buffer is closed via bm_close_buffer(), all
05851 event requests from that buffer are deleted automatically
05852 @param request_id request identifier given by bm_request_event()
05853 @return BM_SUCCESS, BM_INVALID_HANDLE
05854 */
05855 INT bm_delete_request(INT request_id)
05856 {
05857    if (request_id < 0 || request_id >= _request_list_entries)
05858       return BM_INVALID_HANDLE;
05859 
05860    /* remove request entry from buffer */
05861    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
05862 
05863    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
05864 
05865    return BM_SUCCESS;
05866 }
05867 
05868 #if 0                           // currently not used
05869 static void bm_show_pointers(const BUFFER_HEADER * pheader)
05870 {
05871    int i;
05872    const BUFFER_CLIENT *pclient;
05873 
05874    pclient = pheader->client;
05875 
05876    printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
05877           pheader->write_pointer, pheader->size);
05878    for (i = 0; i < pheader->max_client_index; i++)
05879       if (pclient[i].pid) {
05880          printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
05881       }
05882 
05883    printf("done\n");
05884 }
05885 #endif
05886 
05887 static void bm_validate_client_pointers(BUFFER_HEADER * pheader, BUFFER_CLIENT * pclient)
05888 {
05889    assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
05890    assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
05891 
05892    if (pheader->read_pointer <= pheader->write_pointer) {
05893 
05894       if (pclient->read_pointer < pheader->read_pointer) {
05895          cm_msg(MINFO, "bm_validate_client_pointers",
05896                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05897                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05898 
05899          pclient->read_pointer = pheader->read_pointer;
05900       }
05901 
05902       if (pclient->read_pointer > pheader->write_pointer) {
05903          cm_msg(MINFO, "bm_validate_client_pointers",
05904                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05905                 pheader->name, pclient->read_pointer, pheader->write_pointer);
05906 
05907          pclient->read_pointer = pheader->write_pointer;
05908       }
05909 
05910    } else {
05911 
05912       if (pclient->read_pointer < 0) {
05913          cm_msg(MINFO, "bm_validate_client_pointers",
05914                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05915                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05916 
05917          pclient->read_pointer = pheader->read_pointer;
05918       }
05919 
05920       if (pclient->read_pointer >= pheader->size) {
05921          cm_msg(MINFO, "bm_validate_client_pointers",
05922                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05923                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05924 
05925          pclient->read_pointer = pheader->read_pointer;
05926       }
05927 
05928       if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
05929          cm_msg(MINFO, "bm_validate_client_pointers",
05930                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05931                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05932 
05933          pclient->read_pointer = pheader->read_pointer;
05934       }
05935    }
05936 }
05937 
05938 #if 0                           // currently not used
05939 static void bm_validate_pointers(BUFFER_HEADER * pheader)
05940 {
05941    BUFFER_CLIENT *pclient = pheader->client;
05942    int i;
05943 
05944    for (i = 0; i < pheader->max_client_index; i++)
05945       if (pclient[i].pid) {
05946          bm_validate_client_pointers(pheader, &pclient[i]);
05947       }
05948 }
05949 #endif
05950 
05951 static BOOL bm_update_read_pointer(const char *caller_name, BUFFER_HEADER * pheader)
05952 {
05953    BOOL did_move;
05954    int i;
05955    int min_rp;
05956    BUFFER_CLIENT *pclient;
05957 
05958    assert(caller_name);
05959    pclient = pheader->client;
05960 
05961    /* calculate global read pointer as "minimum" of client read pointers */
05962    min_rp = pheader->write_pointer;
05963 
05964    for (i = 0; i < pheader->max_client_index; i++)
05965       if (pclient[i].pid) {
05966 #ifdef DEBUG_MSG
05967          cm_msg(MDEBUG, caller_name, "bm_update_read_pointer: client %d rp=%d", i, pclient[i].read_pointer);
05968 #endif
05969          bm_validate_client_pointers(pheader, &pclient[i]);
05970 
05971          if (pheader->read_pointer <= pheader->write_pointer) {
05972             if (pclient[i].read_pointer < min_rp)
05973                min_rp = pclient[i].read_pointer;
05974          } else {
05975             if (pclient[i].read_pointer <= pheader->write_pointer) {
05976                if (pclient[i].read_pointer < min_rp)
05977                   min_rp = pclient[i].read_pointer;
05978             } else {
05979                int xptr = pclient[i].read_pointer - pheader->size;
05980                if (xptr < min_rp)
05981                   min_rp = xptr;
05982             }
05983          }
05984       }
05985 
05986    if (min_rp < 0)
05987       min_rp += pheader->size;
05988 
05989    assert(min_rp >= 0);
05990    assert(min_rp < pheader->size);
05991 
05992 #ifdef DEBUG_MSG
05993    if (min_rp == pheader->read_pointer)
05994       cm_msg(MDEBUG, caller_name, "bm_update_read_pointer -> wp=%d", pheader->write_pointer);
05995    else
05996       cm_msg(MDEBUG, caller_name, "bm_update_read_pointer -> wp=%d, rp %d -> %d, size=%d",
05997              pheader->write_pointer, pheader->read_pointer, min_rp, pheader->size);
05998 #endif
05999 
06000    did_move = (pheader->read_pointer != min_rp);
06001 
06002    pheader->read_pointer = min_rp;
06003 
06004    return did_move;
06005 }
06006 
06007 static void bm_wakeup_producers(const BUFFER_HEADER * pheader, const BUFFER_CLIENT * pc)
06008 {
06009    int i;
06010    int size;
06011    const BUFFER_CLIENT *pctmp = pheader->client;
06012 
06013    /*
06014       If read pointer has been changed, it may have freed up some space
06015       for waiting producers. So check if free space is now more than 50%
06016       of the buffer size and wake waiting producers.
06017     */
06018 
06019    size = pc->read_pointer - pheader->write_pointer;
06020    if (size <= 0)
06021       size += pheader->size;
06022 
06023    if (size >= pheader->size * 0.5)
06024       for (i = 0; i < pheader->max_client_index; i++, pctmp++)
06025         if (pctmp->pid)
06026            if (pctmp->write_wait < size) {
06027 #ifdef DEBUG_MSG
06028             cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
06029                    pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06030 #endif
06031             ss_resume(pctmp->port, "B  ");
06032             }
06033 }
06034 
06035 static void bm_dispatch_event(int buffer_handle, EVENT_HEADER * pevent)
06036 {
06037    int i;
06038 
06039    /* call dispatcher */
06040    for (i = 0; i < _request_list_entries; i++)
06041       if (_request_list[i].buffer_handle == buffer_handle &&
06042           bm_match_event(_request_list[i].event_id, _request_list[i].trigger_mask, pevent)) {
06043          /* if event is fragmented, call defragmenter */
06044          if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 || (pevent->event_id & 0xF000) == EVENTID_FRAG)
06045             bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), _request_list[i].dispatcher);
06046          else
06047             _request_list[i].dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
06048       }
06049 }
06050 
06051 static void bm_dispatch_from_cache(BUFFER * pbuf, int buffer_handle)
06052 {
06053    EVENT_HEADER *pevent;
06054    int size;
06055 
06056    pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06057    size = pevent->data_size + sizeof(EVENT_HEADER);
06058 
06059    /* correct size for DWORD boundary */
06060    size = ALIGN8(size);
06061 
06062    /* increment read pointer */
06063    pbuf->read_cache_rp += size;
06064 
06065    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06066       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06067 
06068    bm_dispatch_event(buffer_handle, pevent);
06069 }
06070 
06071 static void bm_convert_event_header(EVENT_HEADER * pevent, int convert_flags)
06072 {
06073    /* now convert event header */
06074    if (convert_flags) {
06075       rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
06076       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING, convert_flags);
06077       rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING, convert_flags);
06078       rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING, convert_flags);
06079       rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING, convert_flags);
06080    }
06081 }
06082 
06083 static int bm_copy_from_cache(BUFFER * pbuf, void *destination, int max_size, int *buf_size,
06084                               int convert_flags)
06085 {
06086    int status;
06087    EVENT_HEADER *pevent;
06088    int size;
06089 
06090    pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06091    size = pevent->data_size + sizeof(EVENT_HEADER);
06092 
06093    if (size > max_size) {
06094       memcpy(destination, pevent, max_size);
06095       cm_msg(MERROR, "bm_receive_event", "event size %d larger than buffer size %d", size, max_size);
06096       *buf_size = max_size;
06097       status = BM_TRUNCATED;
06098    } else {
06099       memcpy(destination, pevent, size);
06100       *buf_size = size;
06101       status = BM_SUCCESS;
06102    }
06103 
06104    bm_convert_event_header((EVENT_HEADER *) destination, convert_flags);
06105 
06106    /* correct size for DWORD boundary */
06107    size = ALIGN8(size);
06108 
06109    pbuf->read_cache_rp += size;
06110 
06111    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06112       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06113 
06114    return status;
06115 }
06116 
06117 static int bm_read_cache_has_events(const BUFFER * pbuf)
06118 {
06119    if (pbuf->read_cache_size == 0)
06120       return 0;
06121 
06122    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06123       return 0;
06124 
06125    return 1;
06126 }
06127 
06128 static int bm_wait_for_free_space(int buffer_handle, BUFFER * pbuf, int async_flag, int requested_space)
06129 {
06130    int status;
06131    BUFFER_HEADER *pheader = pbuf->buffer_header;
06132    char *pdata = (char *) (pheader + 1);
06133 
06134    /* make sure the buffer never completely full:
06135     * read pointer and write pointer would coincide
06136     * and the code cannot tell if it means the
06137     * buffer is 100% full or 100% empty. It will explode
06138     * or lose events */
06139    requested_space += 100;
06140 
06141    if (requested_space >= pheader->size)
06142       return BM_NO_MEMORY;
06143 
06144    while (1) {
06145 
06146       BUFFER_CLIENT *pc;
06147       int n_blocking;
06148       int i;
06149       int size;
06150 
06151       /* check if enough space in buffer */
06152 
06153       size = pheader->read_pointer - pheader->write_pointer;
06154       if (size <= 0)
06155          size += pheader->size;
06156 
06157 #if 0
06158       printf
06159           ("bm_send_event: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d\n",
06160            pheader->read_pointer, pheader->write_pointer, size, pheader->size, requested_space);
06161 #endif
06162 
06163       if (requested_space < size)       /* note the '<' to avoid 100% filling */
06164          return BM_SUCCESS;
06165 
06166       /* if not enough space, find out who's blocking */
06167       n_blocking = 0;
06168 
06169       for (i = 0, pc = pheader->client; i < pheader->max_client_index; i++, pc++)
06170          if (pc->pid) {
06171             if (pc->read_pointer == pheader->read_pointer) {
06172                /*
06173                   First assume that the client with the "minimum" read pointer
06174                   is not really blocking due to a GET_ALL request.
06175                 */
06176                BOOL blocking = FALSE;
06177                int blocking_request_id = 0;
06178                int j;
06179 
06180                /* check if this request blocks */
06181 
06182                EVENT_REQUEST *prequest = pc->event_request;
06183                EVENT_HEADER *pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06184 
06185                assert(pc->read_pointer >= 0);
06186                assert(pc->read_pointer <= pheader->size);
06187 
06188                for (j = 0; j < pc->max_request_index; j++, prequest++)
06189                   if (prequest->valid
06190                       && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent_test)) {
06191                      if (prequest->sampling_type & GET_ALL) {
06192                         blocking = TRUE;
06193                         blocking_request_id = prequest->id;
06194                         break;
06195                      }
06196                   }
06197 
06198                if (blocking) {
06199                   n_blocking++;
06200 
06201                   if (pc->read_wait) {
06202                      char str[80];
06203 #ifdef DEBUG_MSG
06204                      cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06205 #endif
06206                      sprintf(str, "B %s %d", pheader->name, blocking_request_id);
06207                      ss_resume(pc->port, str);
06208                   }
06209 
06210                } else {
06211                   /*
06212                      The blocking guy has no GET_ALL request for this event
06213                      -> shift its read pointer.
06214                    */
06215 
06216                   int new_read_pointer;
06217                   int increment =
06218                       sizeof(EVENT_HEADER) + ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
06219 
06220                   /* correct increment for DWORD boundary */
06221                   increment = ALIGN8(increment);
06222 
06223                   assert(increment > 0);
06224                   assert(increment <= pheader->size);
06225 
06226                   new_read_pointer = (pc->read_pointer + increment) % pheader->size;
06227 
06228                   if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06229                      new_read_pointer = 0;
06230 
06231                   pc->read_pointer = new_read_pointer;
06232                }
06233             }
06234          }
06235       /* client loop */
06236       if (n_blocking == 0) {
06237 
06238          BOOL moved;
06239          /*
06240             calculate new global read pointer as "minimum" of
06241             client read pointers
06242           */
06243 
06244          moved = bm_update_read_pointer("bm_send_event", pheader);
06245 
06246          if (!moved) {
06247             cm_msg(MERROR, "bm_wait_for_free_space",
06248                    "BUG: read pointer did not move while waiting for %d bytes, bytes available: %d, buffer size: %d",
06249                    requested_space, size, pheader->size);
06250             return BM_NO_MEMORY;
06251          }
06252 
06253          continue;
06254       }
06255 
06256       /* at least one client is blocking */
06257 
06258       bm_unlock_buffer(buffer_handle);
06259 
06260       /* return now in ASYNC mode */
06261       if (async_flag)
06262          return BM_ASYNC_RETURN;
06263 
06264 #ifdef DEBUG_MSG
06265       cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06266              pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06267 #endif
06268 
06269       /* signal other clients wait mode */
06270       pheader->client[bm_validate_client_index(pbuf)].write_wait = requested_space;
06271 
06272       bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
06273 
06274       status = ss_suspend(1000, MSG_BM);
06275 
06276       /* make sure we do sleep in this loop:
06277        * if we are the mserver receiving data on the event
06278        * socket and the data buffer is full, ss_suspend() will
06279        * never sleep: it will detect data on the event channel,
06280        * call rpc_server_receive() (recursively, we already *are* in
06281        * rpc_server_receive()) and return without sleeping. Result
06282        * is a busy loop waiting for free space in data buffer */
06283       if (status != SS_TIMEOUT)
06284          ss_sleep(10);
06285 
06286       /* validate client index: we could have been removed from the buffer */
06287       pheader->client[bm_validate_client_index(pbuf)].write_wait = 0;
06288 
06289       /* return if TCP connection broken */
06290       if (status == SS_ABORT)
06291          return SS_ABORT;
06292 
06293 #ifdef DEBUG_MSG
06294       cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06295              pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06296 #endif
06297 
06298       bm_lock_buffer(buffer_handle);
06299    }
06300 }
06301 
06302 /********************************************************************/
06303 /**
06304 Sends an event to a buffer.
06305 This function check if the buffer has enough space for the
06306 event, then copies the event to the buffer in shared memory.
06307 If clients have requests for the event, they are notified via an UDP packet.
06308 \code
06309 char event[1000];
06310 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
06311 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
06312 
06313 // set first byte of event
06314 *(event+sizeof(EVENT_HEADER)) = <...>
06315 #include <stdio.h>
06316 #include "midas.h"
06317 main()
06318 {
06319  INT status, i;
06320  HNDLE hbuf;
06321  char event[1000];
06322  status = cm_connect_experiment("", "Sample", "Producer", NULL);
06323  if (status != CM_SUCCESS)
06324  return 1;
06325  bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
06326 
06327  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
06328  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
06329 
06330  // set event data
06331  for (i=0 ; i<100 ; i++)
06332  *(event+sizeof(EVENT_HEADER)+i) = i;
06333  // send event
06334  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
06335  cm_disconnect_experiment();
06336  return 0;
06337 }
06338 \endcode
06339 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06340 @param source Address of event buffer
06341 @param buf_size Size of event including event header in bytes
06342 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06343 blocks if the buffer has not enough free space to receive the event.
06344 If TRUE, the function returns immediately with a
06345 value of BM_ASYNC_RETURN without writing the event to the buffer
06346 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
06347 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
06348 buffer has not enough space to receive event<br>
06349 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
06350 One has to increase MAX_EVENT_SIZE in midas.h and
06351 recompile.
06352 */
06353 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
06354 {
06355    EVENT_HEADER *pevent;
06356 
06357    /* check if event size is invalid */
06358    if (buf_size < sizeof(EVENT_HEADER)) {
06359       cm_msg(MERROR, "bm_send_event", "event size (%d) it too small", buf_size);
06360       return BM_INVALID_PARAM;
06361    }
06362 
06363    /* check if event size defined in header matches buf_size */
06364    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
06365       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
06366              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER)));
06367       return BM_INVALID_PARAM;
06368    }
06369 
06370    if (rpc_is_remote())
06371       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
06372 
06373 #ifdef LOCAL_ROUTINES
06374    {
06375       BUFFER *pbuf;
06376       BUFFER_HEADER *pheader;
06377       BUFFER_CLIENT *pclient;
06378       EVENT_REQUEST *prequest;
06379       INT i, j, size, total_size, status;
06380       INT my_client_index;
06381       INT old_write_pointer;
06382       INT num_requests_client;
06383       char *pdata;
06384       INT request_id;
06385 
06386       pbuf = &_buffer[buffer_handle - 1];
06387 
06388       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06389          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06390          return BM_INVALID_HANDLE;
06391       }
06392 
06393       if (!pbuf->attached) {
06394          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06395          return BM_INVALID_HANDLE;
06396       }
06397 
06398       pevent = (EVENT_HEADER *) source;
06399       total_size = buf_size;
06400 
06401       /* round up total_size to next DWORD boundary */
06402       total_size = ALIGN8(total_size);
06403 
06404       /* look if there is space in the cache */
06405       if (pbuf->write_cache_size) {
06406          status = BM_SUCCESS;
06407 
06408          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
06409             status = bm_flush_cache(buffer_handle, async_flag);
06410 
06411          if (status != BM_SUCCESS)
06412             return status;
06413 
06414          if (total_size < pbuf->write_cache_size) {
06415             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
06416 
06417             pbuf->write_cache_wp += total_size;
06418             return BM_SUCCESS;
06419          }
06420       }
06421 
06422       /* we come here only for events that are too big to fit into the cache */
06423 
06424       /* calculate some shorthands */
06425       pheader = pbuf->buffer_header;
06426       pdata = (char *) (pheader + 1);
06427       my_client_index = bm_validate_client_index(pbuf);
06428       pclient = pheader->client;
06429 
06430       /* check if buffer is large enough */
06431       if (total_size >= pheader->size) {
06432          cm_msg(MERROR, "bm_send_event",
06433                 "total event size (%d) larger than size (%d) of buffer \'%s\'", total_size, pheader->size, pheader->name);
06434          return BM_NO_MEMORY;
06435       }
06436 
06437       /* lock the buffer */
06438       bm_lock_buffer(buffer_handle);
06439 
06440       status = bm_wait_for_free_space(buffer_handle, pbuf, async_flag, total_size);
06441       if (status != BM_SUCCESS) {
06442          bm_unlock_buffer(buffer_handle);
06443          return status;
06444       }
06445 
06446       /* we have space, so let's copy the event */
06447       old_write_pointer = pheader->write_pointer;
06448 
06449       if (pheader->write_pointer + total_size <= pheader->size) {
06450          memcpy(pdata + pheader->write_pointer, pevent, total_size);
06451          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06452          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06453             pheader->write_pointer = 0;
06454       } else {
06455          /* split event */
06456          size = pheader->size - pheader->write_pointer;
06457 
06458          memcpy(pdata + pheader->write_pointer, pevent, size);
06459          memcpy(pdata, (char *) pevent + size, total_size - size);
06460 
06461          pheader->write_pointer = total_size - size;
06462       }
06463 
06464       /* write pointer was incremented, but there should
06465        * always be some free space in the buffer and the
06466        * write pointer should never cacth up to the read pointer:
06467        * the rest of the code gets confused this happens (buffer 100% full)
06468        * as it is write_pointer == read_pointer can be either
06469        * 100% full or 100% empty. My solution: never fill
06470        * the buffer to 100% */
06471       assert(pheader->write_pointer != pheader->read_pointer);
06472 
06473       /* check which clients have a request for this event */
06474       for (i = 0; i < pheader->max_client_index; i++)
06475          if (pclient[i].pid) {
06476             prequest = pclient[i].event_request;
06477             num_requests_client = 0;
06478             request_id = -1;
06479 
06480             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
06481                if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06482                   if (prequest->sampling_type & GET_ALL)
06483                      pclient[i].num_waiting_events++;
06484 
06485                   num_requests_client++;
06486                   request_id = prequest->id;
06487                }
06488 
06489             /* if that client has a request and is suspended, wake it up */
06490             if (num_requests_client && pclient[i].read_wait) {
06491                char str[80];
06492 #ifdef DEBUG_MSG
06493                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06494 #endif
06495                sprintf(str, "B %s %d", pheader->name, request_id);
06496                ss_resume(pclient[i].port, str);
06497             }
06498 
06499             /* if that client has no request, shift its read pointer */
06500             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
06501                pclient[i].read_pointer = pheader->write_pointer;
06502          }
06503 
06504       /* shift read pointer of own client */
06505       if (pclient[my_client_index].read_pointer == old_write_pointer)
06506          pclient[my_client_index].read_pointer = pheader->write_pointer;
06507 
06508       /* calculate global read pointer as "minimum" of client read pointers */
06509 
06510       bm_update_read_pointer("bm_send_event", pheader);
06511 
06512       /* update statistics */
06513       pheader->num_in_events++;
06514 
06515       /* unlock the buffer */
06516       bm_unlock_buffer(buffer_handle);
06517    }
06518 #endif                          /* LOCAL_ROUTINES */
06519 
06520    return BM_SUCCESS;
06521 }
06522 
06523 /********************************************************************/
06524 /**
06525 Empty write cache.
06526 This function should be used if events in the write cache
06527 should be visible to the consumers immediately. It should be called at the
06528 end of each run, otherwise events could be kept in the write buffer and will
06529 flow to the data of the next run.
06530 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06531 @param async_flag Synchronous/asynchronous flag.
06532 If FALSE, the function blocks if the buffer has not
06533 enough free space to receive the full cache. If TRUE, the function returns
06534 immediately with a value of BM_ASYNC_RETURN without writing the cache.
06535 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
06536 BM_ASYNC_RETURN Routine called with async_flag == TRUE
06537 and buffer has not enough space to receive cache<br>
06538 BM_NO_MEMORY Event is too large for network buffer or event buffer.
06539 One has to increase MAX_EVENT_SIZE in midas.h
06540 and recompile.
06541 */
06542 INT bm_flush_cache(INT buffer_handle, INT async_flag)
06543 {
06544    if (rpc_is_remote())
06545       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
06546 
06547 #ifdef LOCAL_ROUTINES
06548    {
06549       BUFFER *pbuf;
06550       BUFFER_HEADER *pheader;
06551       BUFFER_CLIENT *pclient;
06552       EVENT_HEADER *pevent;
06553       INT i, size, total_size, status;
06554       INT my_client_index;
06555       INT old_write_pointer;
06556       char *pdata;
06557 
06558       pbuf = &_buffer[buffer_handle - 1];
06559 
06560       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06561          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06562          return BM_INVALID_HANDLE;
06563       }
06564 
06565       if (!pbuf->attached) {
06566          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06567          return BM_INVALID_HANDLE;
06568       }
06569 
06570       if (pbuf->write_cache_size == 0)
06571          return BM_SUCCESS;
06572 
06573       /* check if anything needs to be flushed */
06574       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
06575          return BM_SUCCESS;
06576 
06577       /* calculate some shorthands */
06578       pheader = _buffer[buffer_handle - 1].buffer_header;
06579       pdata = (char *) (pheader + 1);
06580       my_client_index = bm_validate_client_index(pbuf);
06581       pclient = pheader->client;
06582       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06583 
06584       /* lock the buffer */
06585       bm_lock_buffer(buffer_handle);
06586 
06587 #ifdef DEBUG_MSG
06588       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06589 #endif
06590 
06591       status = bm_wait_for_free_space(buffer_handle, pbuf, async_flag, pbuf->write_cache_wp);
06592       if (status != BM_SUCCESS) {
06593          bm_unlock_buffer(buffer_handle);
06594          return status;
06595       }
06596 
06597       /* we have space, so let's copy the event */
06598       old_write_pointer = pheader->write_pointer;
06599 
06600 #ifdef DEBUG_MSG
06601       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
06602              pheader->write_pointer);
06603 #endif
06604 
06605       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06606          /* loop over all events in cache */
06607 
06608          assert(pbuf->write_cache_rp >= 0);
06609          assert(pbuf->write_cache_rp < pbuf->write_cache_size);
06610 
06611          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06612          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06613 
06614          assert(total_size > 0);
06615          assert(total_size <= pheader->size);
06616 
06617          /* correct size for DWORD boundary */
06618          total_size = ALIGN8(total_size);
06619 
06620          if (pheader->write_pointer + total_size <= pheader->size) {
06621             memcpy(pdata + pheader->write_pointer, pevent, total_size);
06622             pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06623             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06624                pheader->write_pointer = 0;
06625          } else {
06626             /* split event */
06627             size = pheader->size - pheader->write_pointer;
06628 
06629             memcpy(pdata + pheader->write_pointer, pevent, size);
06630             memcpy(pdata, (char *) pevent + size, total_size - size);
06631 
06632             pheader->write_pointer = total_size - size;
06633          }
06634 
06635          /* see comment for the same code in bm_send_event().
06636           * We make sure the buffer is nevere 100% full */
06637          assert(pheader->write_pointer != pheader->read_pointer);
06638 
06639          /* this loop does not loop forever because write_cache_rp
06640           * is monotonously incremented here. write_cache_wp does
06641           * not change */
06642 
06643          pbuf->write_cache_rp += total_size;
06644       }
06645 
06646       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06647 
06648       /* check which clients are waiting */
06649       for (i = 0; i < pheader->max_client_index; i++)
06650          if (pclient[i].pid && pclient[i].read_wait) {
06651             char str[80];
06652 #ifdef DEBUG_MSG
06653             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06654 #endif
06655             sprintf(str, "B %s %d", pheader->name, -1);
06656             ss_resume(pclient[i].port, str);
06657          }
06658 
06659       /* shift read pointer of own client */
06660       if (pclient[my_client_index].read_pointer == old_write_pointer)
06661          pclient[my_client_index].read_pointer = pheader->write_pointer;
06662 
06663       /* calculate global read pointer as "minimum" of client read pointers */
06664 
06665       bm_update_read_pointer("bm_flush_cache", pheader);
06666 
06667       /* update statistics */
06668       pheader->num_in_events++;
06669 
06670       /* unlock the buffer */
06671       bm_unlock_buffer(buffer_handle);
06672    }
06673 #endif                          /* LOCAL_ROUTINES */
06674 
06675    return BM_SUCCESS;
06676 }
06677 
06678 /********************************************************************/
06679 /**
06680 Receives events directly.
06681 This function is an alternative way to receive events without
06682 a main loop.
06683 
06684 It can be used in analysis systems which actively receive events,
06685 rather than using callbacks. A analysis package could for example contain its own
06686 command line interface. A command
06687 like "receive 1000 events" could make it necessary to call bm_receive_event()
06688 1000 times in a row to receive these events and then return back to the
06689 command line prompt.
06690 The according bm_request_event() call contains NULL as the
06691 callback routine to indicate that bm_receive_event() is called to receive
06692 events.
06693 \code
06694 #include <stdio.h>
06695 #include "midas.h"
06696 void process_event(EVENT_HEADER *pheader)
06697 {
06698  printf("Received event #%d\r",
06699  pheader->serial_number);
06700 }
06701 main()
06702 {
06703   INT status, request_id;
06704   HNDLE hbuf;
06705   char event_buffer[1000];
06706   status = cm_connect_experiment("", "Sample",
06707   "Simple Analyzer", NULL);
06708   if (status != CM_SUCCESS)
06709    return 1;
06710   bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
06711   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06712 
06713   do
06714   {
06715    size = sizeof(event_buffer);
06716    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06717   if (status == CM_SUCCESS)
06718    process_event((EVENT_HEADER *) event_buffer);
06719    <...do something else...>
06720    status = cm_yield(0);
06721   } while (status != RPC_SHUTDOWN &&
06722   status != SS_ABORT);
06723   cm_disconnect_experiment();
06724   return 0;
06725 }
06726 \endcode
06727 @param buffer_handle buffer handle
06728 @param destination destination address where event is written to
06729 @param buf_size size of destination buffer on input, size of event plus
06730 header on return.
06731 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06732 blocks if no event is available. If TRUE, the function returns immediately
06733 with a value of BM_ASYNC_RETURN without receiving any event.
06734 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06735 BM_TRUNCATED   The event is larger than the destination buffer and was
06736                therefore truncated <br>
06737 BM_ASYNC_RETURN No event available
06738 */
06739 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06740 {
06741    if (rpc_is_remote()) {
06742       int status, old_timeout = 0;
06743 
06744       status = resize_net_send_buffer("bm_receive_event", *buf_size);
06745       if (status != SUCCESS)
06746          return status;  
06747 
06748       if (!async_flag) {
06749          old_timeout = rpc_get_option(-1, RPC_OTIMEOUT);
06750          rpc_set_option(-1, RPC_OTIMEOUT, 0);
06751       }
06752 
06753       status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, destination, buf_size, async_flag);
06754 
06755       if (!async_flag) {
06756          rpc_set_option(-1, RPC_OTIMEOUT, old_timeout);
06757       }
06758 
06759       return status;
06760    }
06761 #ifdef LOCAL_ROUTINES
06762    {
06763       BUFFER *pbuf;
06764       BUFFER_HEADER *pheader;
06765       BUFFER_CLIENT *pclient, *pc;
06766       char *pdata;
06767       INT convert_flags;
06768       INT i, size, max_size;
06769       INT status = 0;
06770       INT my_client_index;
06771       BOOL cache_is_full = FALSE;
06772       BOOL use_event_buffer = FALSE;
06773       int cycle = 0;
06774 
06775       pbuf = &_buffer[buffer_handle - 1];
06776 
06777       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06778          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06779          return BM_INVALID_HANDLE;
06780       }
06781 
06782       if (!pbuf->attached) {
06783          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06784          return BM_INVALID_HANDLE;
06785       }
06786 
06787       max_size = *buf_size;
06788       *buf_size = 0;
06789 
06790       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06791          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06792       else
06793          convert_flags = 0;
06794 
06795       /* look if there is anything in the cache */
06796       if (bm_read_cache_has_events(pbuf))
06797          return bm_copy_from_cache(pbuf, destination, max_size, buf_size, convert_flags);
06798 
06799     LOOP:
06800       /* make sure we do bm_validate_client_index() after sleeping */
06801 
06802       /* calculate some shorthands */
06803       pheader = pbuf->buffer_header;
06804       pdata = (char *) (pheader + 1);
06805       my_client_index = bm_validate_client_index(pbuf);
06806       pclient = pheader->client;
06807       pc = pheader->client + my_client_index;
06808 
06809       /* first do a quick check without locking the buffer */
06810       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06811          return BM_ASYNC_RETURN;
06812 
06813       /* lock the buffer */
06814       bm_lock_buffer(buffer_handle);
06815 
06816       while (pheader->write_pointer == pc->read_pointer) {
06817 
06818          bm_unlock_buffer(buffer_handle);
06819 
06820          /* return now in ASYNC mode */
06821          if (async_flag == ASYNC)
06822             return BM_ASYNC_RETURN;
06823 
06824          pc->read_wait = TRUE;
06825 
06826          /* check again pointers (may have moved in between) */
06827          if (pheader->write_pointer == pc->read_pointer) {
06828 #ifdef DEBUG_MSG
06829             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06830                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06831 #endif
06832 
06833             status = ss_suspend(1000, MSG_BM);
06834 
06835 #ifdef DEBUG_MSG
06836             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06837 #endif
06838 
06839             /* return if TCP connection broken */
06840             if (status == SS_ABORT)
06841                return SS_ABORT;
06842          }
06843 
06844          pc->read_wait = FALSE;
06845 
06846          /* validate client_index: somebody may have disconnected us from the buffer */
06847          bm_validate_client_index(pbuf);
06848 
06849          bm_lock_buffer(buffer_handle);
06850       }
06851 
06852       /* check if event at current read pointer matches a request */
06853 
06854       do {
06855          int new_read_pointer;
06856          int total_size;        /* size of the event */
06857          EVENT_REQUEST *prequest;
06858          EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06859 
06860          assert(pc->read_pointer >= 0);
06861          assert(pc->read_pointer <= pheader->size);
06862 
06863          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06864          total_size = ALIGN8(total_size);
06865 
06866          assert(total_size > 0);
06867          assert(total_size <= pheader->size);
06868 
06869          prequest = pc->event_request;
06870 
06871          /* loop over all requests: if this event matches a request,
06872           * copy it to the read cache */
06873 
06874          for (i = 0; i < pc->max_request_index; i++, prequest++)
06875             if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06876 
06877                /* we found a request for this event, so copy it */
06878 
06879                if (pbuf->read_cache_size > 0 && total_size < pbuf->read_cache_size) {
06880 
06881                   /* copy event to cache, if there is room */
06882 
06883                   if (pbuf->read_cache_wp + total_size >= pbuf->read_cache_size) {
06884                      cache_is_full = TRUE;
06885                      break;     /* exit loop over requests */
06886                   }
06887 
06888                   if (pc->read_pointer + total_size <= pheader->size) {
06889                      /* copy event to cache */
06890                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06891                   } else {
06892                      /* event is splitted */
06893                      size = pheader->size - pc->read_pointer;
06894                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06895                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size, pdata, total_size - size);
06896                   }
06897 
06898                   pbuf->read_cache_wp += total_size;
06899 
06900                } else {
06901                   int copy_size = total_size;
06902 
06903                   /* if there are events in the read cache,
06904                    * we should dispatch them before we
06905                    * despatch this oversize event */
06906 
06907                   if (bm_read_cache_has_events(pbuf)) {
06908                      cache_is_full = TRUE;
06909                      break;     /* exit loop over requests */
06910                   }
06911 
06912                   use_event_buffer = TRUE;
06913 
06914                   status = BM_SUCCESS;
06915                   if (copy_size > max_size) {
06916                      copy_size = max_size;
06917                      cm_msg(MERROR, "bm_receive_event",
06918                             "event size %d larger than buffer size %d", total_size, max_size);
06919                      status = BM_TRUNCATED;
06920                   }
06921 
06922                   if (pc->read_pointer + total_size <= pheader->size) {
06923                      /* event is not splitted */
06924                      memcpy(destination, pevent, copy_size);
06925                   } else {
06926                      /* event is splitted */
06927                      size = pheader->size - pc->read_pointer;
06928 
06929                      if (size > max_size)
06930                         memcpy(destination, pevent, max_size);
06931                      else
06932                         memcpy(destination, pevent, size);
06933 
06934                      if (total_size > max_size) {
06935                         if (size <= max_size)
06936                            memcpy((char *) destination + size, pdata, max_size - size);
06937                      } else
06938                         memcpy((char *) destination + size, pdata, total_size - size);
06939                   }
06940 
06941                   *buf_size = copy_size;
06942 
06943                   bm_convert_event_header((EVENT_HEADER *) destination, convert_flags);
06944                }
06945 
06946                /* update statistics */
06947                pheader->num_out_events++;
06948                break;           /* stop looping over requests */
06949             }
06950 
06951          if (cache_is_full)
06952             break;              /* exit from loop over events in data buffer, leaving the current event untouched */
06953 
06954          /* shift read pointer */
06955 
06956          assert(total_size > 0);
06957          assert(total_size <= pheader->size);
06958 
06959          new_read_pointer = pc->read_pointer + total_size;
06960          if (new_read_pointer >= pheader->size) {
06961             new_read_pointer = new_read_pointer % pheader->size;
06962 
06963             /* make sure we loop over the data buffer no more than once */
06964             cycle++;
06965             assert(cycle < 2);
06966          }
06967 
06968          /* make sure we do not split the event header at the end of the buffer */
06969          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06970             new_read_pointer = 0;
06971 
06972 #ifdef DEBUG_MSG
06973          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06974                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found, total_size);
06975 #endif
06976 
06977          pc->read_pointer = new_read_pointer;
06978 
06979          if (use_event_buffer)
06980             break;              /* exit from loop over events in data buffer */
06981 
06982       } while (pheader->write_pointer != pc->read_pointer);
06983 
06984       /* calculate global read pointer as "minimum" of client read pointers */
06985 
06986       bm_update_read_pointer("bm_receive_event", pheader);
06987 
06988       /*
06989          If read pointer has been changed, it may have freed up some space
06990          for waiting producers. So check if free space is now more than 50%
06991          of the buffer size and wake waiting producers.
06992        */
06993 
06994       bm_wakeup_producers(pheader, pc);
06995 
06996       bm_unlock_buffer(buffer_handle);
06997 
06998       if (bm_read_cache_has_events(pbuf)) {
06999          assert(!use_event_buffer);     /* events only go into the _event_buffer when read cache is empty */
07000          return bm_copy_from_cache(pbuf, destination, max_size, buf_size, convert_flags);
07001       }
07002 
07003       if (use_event_buffer)
07004          return status;
07005 
07006       goto LOOP;
07007    }
07008 #else                           /* LOCAL_ROUTINES */
07009 
07010    return SS_SUCCESS;
07011 #endif
07012 }
07013 
07014 /********************************************************************/
07015 /**
07016 Skip all events in current buffer.
07017 
07018 Useful for single event displays to see the newest events
07019 @param buffer_handle      Handle of the buffer. Must be obtained
07020                           via bm_open_buffer.
07021 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR
07022 */
07023 INT bm_skip_event(INT buffer_handle)
07024 {
07025    if (rpc_is_remote())
07026       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
07027 
07028 #ifdef LOCAL_ROUTINES
07029    {
07030       BUFFER *pbuf;
07031       BUFFER_HEADER *pheader;
07032       BUFFER_CLIENT *pclient;
07033 
07034       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
07035          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07036          return BM_INVALID_HANDLE;
07037       }
07038 
07039       pbuf = &_buffer[buffer_handle - 1];
07040       pheader = pbuf->buffer_header;
07041 
07042       if (!pbuf->attached) {
07043          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07044          return BM_INVALID_HANDLE;
07045       }
07046 
07047       /* clear cache */
07048       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
07049          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07050 
07051       bm_lock_buffer(buffer_handle);
07052 
07053       /* forward read pointer to global write pointer */
07054       pclient = pheader->client + bm_validate_client_index(pbuf);
07055       pclient->read_pointer = pheader->write_pointer;
07056 
07057       bm_unlock_buffer(buffer_handle);
07058    }
07059 #endif
07060 
07061    return BM_SUCCESS;
07062 }
07063 
07064 /********************************************************************/
07065 /**
07066 Check a buffer if an event is available and call the dispatch function if found.
07067 @param buffer_name       Name of buffer
07068 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN,
07069                     RPC_NET_ERROR
07070 */
07071 INT bm_push_event(char *buffer_name)
07072 {
07073 #ifdef LOCAL_ROUTINES
07074    {
07075       BUFFER *pbuf;
07076       BUFFER_HEADER *pheader;
07077       BUFFER_CLIENT *pclient, *pc;
07078       char *pdata;
07079       INT i, size, buffer_handle;
07080       INT my_client_index;
07081       BOOL use_event_buffer = 0;
07082       BOOL cache_is_full = 0;
07083       int cycle = 0;
07084 
07085       for (i = 0; i < _buffer_entries; i++)
07086          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07087             break;
07088       if (i == _buffer_entries)
07089          return BM_INVALID_HANDLE;
07090 
07091       buffer_handle = i + 1;
07092       pbuf = &_buffer[buffer_handle - 1];
07093 
07094       if (!pbuf->attached)
07095          return BM_INVALID_HANDLE;
07096 
07097       /* return immediately if no callback routine is defined */
07098       if (!pbuf->callback)
07099          return BM_SUCCESS;
07100 
07101       if (_event_buffer_size == 0) {
07102          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
07103          if (_event_buffer == NULL) {
07104             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
07105             return BM_NO_MEMORY;
07106          }
07107          _event_buffer_size = 1000;
07108       }
07109 
07110       /* look if there is anything in the cache */
07111       if (bm_read_cache_has_events(pbuf)) {
07112          bm_dispatch_from_cache(pbuf, buffer_handle);
07113          return BM_MORE_EVENTS;
07114       }
07115 
07116       /* calculate some shorthands */
07117       pheader = pbuf->buffer_header;
07118       pdata = (char *) (pheader + 1);
07119       my_client_index = bm_validate_client_index(pbuf);
07120       pclient = pheader->client;
07121       pc = pheader->client + my_client_index;
07122 
07123       /* first do a quick check without locking the buffer */
07124       if (pheader->write_pointer == pc->read_pointer)
07125          return BM_SUCCESS;
07126 
07127       /* lock the buffer */
07128       bm_lock_buffer(buffer_handle);
07129 
07130       if (pheader->write_pointer == pc->read_pointer) {
07131 
07132          bm_unlock_buffer(buffer_handle);
07133 
07134          /* return if no event available */
07135          return BM_SUCCESS;
07136       }
07137 
07138       /* loop over all events in the buffer */
07139 
07140       do {
07141          int new_read_pointer;
07142          int total_size;        /* size of the event */
07143          EVENT_REQUEST *prequest;
07144          EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
07145 
07146          assert(pc->read_pointer >= 0);
07147          assert(pc->read_pointer <= pheader->size);
07148 
07149          total_size = pevent->data_size + sizeof(EVENT_HEADER);
07150          total_size = ALIGN8(total_size);
07151 
07152          assert(total_size > 0);
07153          assert(total_size <= pheader->size);
07154 
07155          prequest = pc->event_request;
07156 
07157          /* loop over all requests: if this event matches a request,
07158           * copy it to the read cache */
07159 
07160          for (i = 0; i < pc->max_request_index; i++, prequest++)
07161             if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
07162 
07163                /* check if this is a recent event */
07164                if (prequest->sampling_type == GET_RECENT) {
07165                   if (ss_time() - pevent->time_stamp > 1) {
07166                      /* skip that event */
07167                      continue;
07168                   }
07169 
07170                   printf("now: %d, event: %d\n", ss_time(), pevent->time_stamp);
07171                }
07172 
07173                /* we found a request for this event, so copy it */
07174 
07175                if (pbuf->read_cache_size > 0 && total_size < pbuf->read_cache_size) {
07176 
07177                   /* copy event to cache, if there is room */
07178 
07179                   if (pbuf->read_cache_wp + total_size >= pbuf->read_cache_size) {
07180                      cache_is_full = TRUE;
07181                      break;     /* exit loop over requests */
07182                   }
07183 
07184                   if (pc->read_pointer + total_size <= pheader->size) {
07185                      /* copy event to cache */
07186                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
07187                   } else {
07188                      /* event is splitted */
07189                      size = pheader->size - pc->read_pointer;
07190                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
07191                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size, pdata, total_size - size);
07192                   }
07193 
07194                   pbuf->read_cache_wp += total_size;
07195 
07196                } else {
07197                   /* copy event to copy buffer */
07198 
07199                   /* if there are events in the read cache,
07200                    * we should dispatch them before we
07201                    * despatch this oversize event */
07202 
07203                   if (bm_read_cache_has_events(pbuf)) {
07204                      cache_is_full = TRUE;
07205                      break;     /* exit loop over requests */
07206                   }
07207 
07208                   use_event_buffer = TRUE;
07209 
07210                   if (total_size > _event_buffer_size) {
07211                      //printf("realloc event buffer %d -> %d\n", _event_buffer_size, total_size);
07212                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
07213                      _event_buffer_size = total_size;
07214                   }
07215 
07216                   if (pc->read_pointer + total_size <= pheader->size) {
07217                      memcpy(_event_buffer, pevent, total_size);
07218                   } else {
07219                      /* event is splitted */
07220                      size = pheader->size - pc->read_pointer;
07221 
07222                      memcpy(_event_buffer, pevent, size);
07223                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
07224                   }
07225                }
07226 
07227                /* update statistics */
07228                pheader->num_out_events++;
07229                break;           /* exit loop over event requests */
07230 
07231             }
07232          /* end of loop over event requests */
07233          if (cache_is_full)
07234             break;              /* exit from loop over events in data buffer, leaving the current event untouched */
07235 
07236          /* shift read pointer */
07237 
07238          assert(total_size > 0);
07239          assert(total_size <= pheader->size);
07240 
07241          new_read_pointer = pc->read_pointer + total_size;
07242          if (new_read_pointer >= pheader->size) {
07243             new_read_pointer = new_read_pointer % pheader->size;
07244 
07245             /* make sure we loop over the data buffer no more than once */
07246             cycle++;
07247             assert(cycle < 2);
07248          }
07249 
07250          /* make sure we do not split the event header at the end of the buffer */
07251          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
07252             new_read_pointer = 0;
07253 
07254 #ifdef DEBUG_MSG
07255          cm_msg(MDEBUG, "bm_push_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
07256                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found, total_size);
07257 #endif
07258 
07259          pc->read_pointer = new_read_pointer;
07260 
07261          if (use_event_buffer)
07262             break;              /* exit from loop over events in data buffer */
07263 
07264       } while (pheader->write_pointer != pc->read_pointer);
07265 
07266       /* calculate global read pointer as "minimum" of client read pointers */
07267 
07268       bm_update_read_pointer("bm_push_event", pheader);
07269 
07270       /*
07271          If read pointer has been changed, it may have freed up some space
07272          for waiting producers. So check if free space is now more than 50%
07273          of the buffer size and wake waiting producers.
07274        */
07275 
07276       bm_wakeup_producers(pheader, pc);
07277 
07278       bm_unlock_buffer(buffer_handle);
07279 
07280       if (bm_read_cache_has_events(pbuf)) {
07281          assert(!use_event_buffer);     /* events only go into the _event_buffer when read cache is empty */
07282          bm_dispatch_from_cache(pbuf, buffer_handle);
07283          return BM_MORE_EVENTS;
07284       }
07285 
07286       if (use_event_buffer) {
07287          bm_dispatch_event(buffer_handle, _event_buffer);
07288          return BM_MORE_EVENTS;
07289       }
07290 
07291       return BM_SUCCESS;
07292    }
07293 #else                           /* LOCAL_ROUTINES */
07294 
07295    return BM_SUCCESS;
07296 #endif
07297 }
07298 
07299 /********************************************************************/
07300 /**
07301 Check if any requested event is waiting in a buffer
07302 @return TRUE             More events are waiting<br>
07303         FALSE            No more events are waiting
07304 */
07305 INT bm_check_buffers()
07306 {
07307 #ifdef LOCAL_ROUTINES
07308    {
07309       INT idx, status = 0;
07310       INT server_type, server_conn, tid;
07311       BOOL bMore;
07312       DWORD start_time;
07313 
07314       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07315       server_conn = rpc_get_server_acception();
07316       tid = ss_gettid();
07317 
07318       /* if running as a server, buffer checking is done by client
07319          via ASYNC bm_receive_event */
07320       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
07321          return FALSE;
07322 
07323       bMore = FALSE;
07324       start_time = ss_millitime();
07325 
07326       /* go through all buffers */
07327       for (idx = 0; idx < _buffer_entries; idx++) {
07328          if (server_type == ST_SINGLE && _buffer[idx].index != server_conn)
07329             continue;
07330 
07331          if (server_type != ST_SINGLE && _buffer[idx].index != tid)
07332             continue;
07333 
07334          if (!_buffer[idx].attached)
07335             continue;
07336 
07337          do {
07338 
07339             /* one bm_push_event could cause a run stop and a buffer close, which
07340                would crash the next call to bm_push_event(). So check for valid
07341                buffer on each call */
07342             if (idx < _buffer_entries && _buffer[idx].buffer_header->name != NULL)
07343                status = bm_push_event(_buffer[idx].buffer_header->name);
07344 
07345             if (status != BM_MORE_EVENTS)
07346                break;
07347 
07348             /* stop after one second */
07349             if (ss_millitime() - start_time > 1000) {
07350                bMore = TRUE;
07351                break;
07352             }
07353 
07354          } while (TRUE);
07355       }
07356 
07357       return bMore;
07358 
07359    }
07360 #else                           /* LOCAL_ROUTINES */
07361 
07362    return FALSE;
07363 
07364 #endif
07365 }
07366 
07367 /**dox***************************************************************/
07368 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07369 
07370 /********************************************************************/
07371 INT bm_mark_read_waiting(BOOL flag)
07372 /********************************************************************\
07373 
07374   Routine: bm_mark_read_waiting
07375 
07376   Purpose: Mark all open buffers ready for receiving events.
07377            Called internally by ss_suspend
07378 
07379 
07380   Input:
07381     BOOL flag               TRUE for waiting, FALSE for not waiting
07382 
07383   Output:
07384     none
07385 
07386   Function value:
07387     BM_SUCCESS              Successful completion
07388 
07389 \********************************************************************/
07390 {
07391    if (rpc_is_remote())
07392       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
07393 
07394 #ifdef LOCAL_ROUTINES
07395    {
07396       INT i;
07397       BUFFER_HEADER *pheader;
07398       BUFFER_CLIENT *pclient;
07399 
07400       /* Mark all buffer for read waiting */
07401       for (i = 0; i < _buffer_entries; i++) {
07402          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE
07403              && _buffer[i].index != rpc_get_server_acception())
07404             continue;
07405 
07406          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i].index != ss_gettid())
07407             continue;
07408 
07409          if (!_buffer[i].attached)
07410             continue;
07411 
07412          pheader = _buffer[i].buffer_header;
07413          pclient = pheader->client + bm_validate_client_index(&_buffer[i]);
07414          pclient->read_wait = flag;
07415       }
07416    }
07417 #endif                          /* LOCAL_ROUTINES */
07418 
07419    return BM_SUCCESS;
07420 }
07421 
07422 /********************************************************************/
07423 INT bm_notify_client(char *buffer_name, int s)
07424 /********************************************************************\
07425 
07426   Routine: bm_notify_client
07427 
07428   Purpose: Called by cm_dispatch_ipc. Send an event notification to
07429            the connected client
07430 
07431   Input:
07432     char  *buffer_name      Name of buffer
07433     int   s                 Network socket to client
07434 
07435   Output:
07436     none
07437 
07438   Function value:
07439     BM_SUCCESS              Successful completion
07440 
07441 \********************************************************************/
07442 {
07443    char buffer[32];
07444    NET_COMMAND *nc;
07445    INT i, convert_flags;
07446    static DWORD last_time = 0;
07447 
07448    for (i = 0; i < _buffer_entries; i++)
07449       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07450          break;
07451    if (i == _buffer_entries)
07452       return BM_INVALID_HANDLE;
07453 
07454    /* don't send notification if client has no callback defined
07455       to receive events -> client calls bm_receive_event manually */
07456    if (!_buffer[i].callback)
07457       return DB_SUCCESS;
07458 
07459    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
07460 
07461    /* only send notification once each 500ms */
07462    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
07463       return DB_SUCCESS;
07464 
07465    last_time = ss_millitime();
07466 
07467    if (convert_flags & CF_ASCII) {
07468       sprintf(buffer, "MSG_BM&%s", buffer_name);
07469       send_tcp(s, buffer, strlen(buffer) + 1, 0);
07470    } else {
07471       nc = (NET_COMMAND *) buffer;
07472 
07473       nc->header.routine_id = MSG_BM;
07474       nc->header.param_size = 0;
07475 
07476       if (convert_flags) {
07477          rpc_convert_single(&nc->header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
07478          rpc_convert_single(&nc->header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
07479       }
07480 
07481       /* send the update notification to the client */
07482       send_tcp(s, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
07483    }
07484 
07485    return BM_SUCCESS;
07486 }
07487 
07488 /********************************************************************/
07489 INT bm_poll_event(INT flag)
07490 /********************************************************************\
07491 
07492   Routine: bm_poll_event
07493 
07494   Purpose: Poll an event from a remote server. Gets called by
07495            rpc_client_dispatch
07496 
07497   Input:
07498     INT flag         TRUE if called from cm_yield
07499 
07500   Output:
07501     none
07502 
07503   Function value:
07504     TRUE             More events are waiting
07505     FALSE            No more events are waiting
07506     SS_ABORT         Network connection broken
07507 
07508 \********************************************************************/
07509 {
07510    INT status, size, i, request_id;
07511    DWORD start_time;
07512    BOOL bMore;
07513    static BOOL bMoreLast = FALSE;
07514 
07515    if (_event_buffer_size == 0) {
07516       HNDLE hDB, odb_key;
07517       int max_event_size = MAX_EVENT_SIZE;
07518 
07519       /* get max event size from ODB */
07520       status = cm_get_experiment_database(&hDB, &odb_key);
07521       assert(status == SUCCESS);
07522       size = sizeof(INT);
07523       status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &max_event_size, &size, TID_DWORD, TRUE);
07524 
07525       size = max_event_size + sizeof(EVENT_HEADER);
07526       //printf("alloc event buffer %d\n", size);
07527       _event_buffer = (EVENT_HEADER *) M_MALLOC(size);
07528       if (!_event_buffer) {
07529          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer of size %d", size);
07530          return SS_ABORT;
07531       }
07532       _event_buffer_size = size;
07533    }
07534 
07535    start_time = ss_millitime();
07536 
07537    /* if we got event notification, turn off read_wait */
07538    if (!flag)
07539       bm_mark_read_waiting(FALSE);
07540 
07541    /* if called from yield, return if no more events */
07542    if (flag) {
07543       if (!bMoreLast)
07544          return FALSE;
07545    }
07546 
07547    bMore = FALSE;
07548 
07549    /* loop over all requests */
07550    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07551       /* continue if no dispatcher set (manual bm_receive_event) */
07552       if (_request_list[request_id].dispatcher == NULL)
07553          continue;
07554 
07555       do {
07556          /* receive event */
07557          size = _event_buffer_size;
07558          status = bm_receive_event(_request_list[request_id].buffer_handle, _event_buffer, &size, ASYNC);
07559          //printf("receive_event buf %d, event %d, status %d\n", _event_buffer_size, size, status);
07560 
07561          /* call user function if successful */
07562          if (status == BM_SUCCESS)
07563             /* search proper request for this event */
07564             for (i = 0; i < _request_list_entries; i++)
07565                if ((_request_list[i].buffer_handle ==
07566                     _request_list[request_id].buffer_handle) &&
07567                    bm_match_event(_request_list[i].event_id, _request_list[i].trigger_mask, _event_buffer)) {
07568                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07569                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07570                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07571                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07572                                          _request_list[i].dispatcher);
07573                   else
07574                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07575                                                  _event_buffer,
07576                                                  (void *) (((EVENT_HEADER *) _event_buffer) + 1));
07577                }
07578 
07579          /* break if no more events */
07580          if (status == BM_ASYNC_RETURN)
07581             break;
07582 
07583          /* break if server died */
07584          if (status == RPC_NET_ERROR)
07585             return SS_ABORT;
07586 
07587          /* stop after one second */
07588          if (ss_millitime() - start_time > 1000) {
07589             bMore = TRUE;
07590             break;
07591          }
07592 
07593       } while (TRUE);
07594    }
07595 
07596    if (!bMore)
07597       bm_mark_read_waiting(TRUE);
07598 
07599    bMoreLast = bMore;
07600 
07601    return bMore;
07602 }
07603 
07604 /**dox***************************************************************/
07605 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07606 
07607 /********************************************************************/
07608 /**
07609 Clears event buffer and cache.
07610 If an event buffer is large and a consumer is slow in analyzing
07611 events, events are usually received some time after they are produced.
07612 This effect is even more experienced if a read cache is used
07613 (via bm_set_cache_size()).
07614 When changes to the hardware are made in the experience, the consumer will then
07615 still analyze old events before any new event which reflects the hardware change.
07616 Users can be fooled by looking at histograms which reflect the hardware change
07617 many seconds after they have been made.
07618 
07619 To overcome this potential problem, the analyzer can call
07620 bm_empty_buffers() just after the hardware change has been made which
07621 skips all old events contained in event buffers and read caches.
07622 Technically this is done by forwarding the read pointer of the client.
07623 No events are really deleted, they are still visible to other clients like
07624 the logger.
07625 
07626 Note that the front-end also contains write buffers which can delay the
07627 delivery of events.
07628 The standard front-end framework mfe.c reduces this effect by flushing
07629 all buffers once every second.
07630 @return BM_SUCCESS
07631 */
07632 INT bm_empty_buffers()
07633 {
07634    if (rpc_is_remote())
07635       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07636 
07637 #ifdef LOCAL_ROUTINES
07638    {
07639       INT idx, server_type, server_conn, tid;
07640       BUFFER *pbuf;
07641       BUFFER_CLIENT *pclient;
07642 
07643       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07644       server_conn = rpc_get_server_acception();
07645       tid = ss_gettid();
07646 
07647       /* go through all buffers */
07648       for (idx = 0; idx < _buffer_entries; idx++) {
07649          if (server_type == ST_SINGLE && _buffer[idx].index != server_conn)
07650             continue;
07651 
07652          if (server_type != ST_SINGLE && _buffer[idx].index != tid)
07653             continue;
07654 
07655          if (!_buffer[idx].attached)
07656             continue;
07657 
07658          pbuf = &_buffer[idx];
07659 
07660          /* empty cache */
07661          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07662 
07663          /* set read pointer to write pointer */
07664          pclient = (pbuf->buffer_header)->client + bm_validate_client_index(pbuf);
07665          bm_lock_buffer(idx + 1);
07666          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07667          bm_unlock_buffer(idx + 1);
07668       }
07669 
07670    }
07671 #endif                          /* LOCAL_ROUTINES */
07672 
07673    return BM_SUCCESS;
07674 }
07675 
07676 /**dox***************************************************************/
07677 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07678 
07679 #define MAX_DEFRAG_EVENTS 10
07680 
07681 typedef struct {
07682    WORD event_id;
07683    DWORD data_size;
07684    DWORD received;
07685    EVENT_HEADER *pevent;
07686 } EVENT_DEFRAG_BUFFER;
07687 
07688 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07689 
07690 /********************************************************************/
07691 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07692                          EVENT_HEADER * pevent, void *pdata, void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *,
07693                                                                                  void *))
07694 /********************************************************************\
07695 
07696   Routine: bm_defragment_event
07697 
07698   Purpose: Called internally from the event receiving routines
07699            bm_push_event and bm_poll_event to recombine event
07700            fragments and call the user callback routine upon
07701            completion.
07702 
07703   Input:
07704     HNDLE buffer_handle  Handle for the buffer containing event
07705     HNDLE request_id     Handle for event request
07706     EVENT_HEADER *pevent Pointer to event header
07707     void *pata           Pointer to event data
07708     dispatcher()         User callback routine
07709 
07710   Output:
07711     <calls dispatcher() after successfull recombination of event>
07712 
07713   Function value:
07714     void
07715 
07716 \********************************************************************/
07717 {
07718    INT i;
07719    static int j = -1;
07720 
07721    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07722       /*---- start new event ----*/
07723 
07724       //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
07725 
07726       /* check if fragments already stored */
07727       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07728          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07729             break;
07730 
07731       if (i < MAX_DEFRAG_EVENTS) {
07732          free(defrag_buffer[i].pevent);
07733          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07734          cm_msg(MERROR, "bm_defragement_event",
07735                 "Received new event with ID %d while old fragments were not completed",
07736                 (pevent->event_id & 0x0FFF));
07737       }
07738 
07739       /* search new slot */
07740       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07741          if (defrag_buffer[i].event_id == 0)
07742             break;
07743 
07744       if (i == MAX_DEFRAG_EVENTS) {
07745          cm_msg(MERROR, "bm_defragment_event",
07746                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07747          return;
07748       }
07749 
07750       /* check event size */
07751       if (pevent->data_size != sizeof(DWORD)) {
07752          cm_msg(MERROR, "bm_defragment_event",
07753                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07754                 pevent->data_size, sizeof(DWORD));
07755          return;
07756       }
07757 
07758       /* setup defragment buffer */
07759       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07760       defrag_buffer[i].data_size = *(DWORD *) pdata;
07761       defrag_buffer[i].received = 0;
07762       defrag_buffer[i].pevent = (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07763 
07764       if (defrag_buffer[i].pevent == NULL) {
07765          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07766          cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
07767          return;
07768       }
07769 
07770       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07771       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07772       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07773 
07774       // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
07775       //       pevent->serial_number, defrag_buffer[i].data_size);
07776 
07777       j = 0;
07778 
07779       return;
07780    }
07781 
07782    /* search buffer for that event */
07783    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07784       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07785          break;
07786 
07787    if (i == MAX_DEFRAG_EVENTS) {
07788       /* no buffer available -> no first fragment received */
07789       cm_msg(MERROR, "bm_defragement_event",
07790              "Received fragment without first fragment (ID %d) Ser#:%d",
07791              pevent->event_id & 0x0FFF, pevent->serial_number);
07792       return;
07793    }
07794 
07795    /* add fragment to buffer */
07796    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
07797       free(defrag_buffer[i].pevent);
07798       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07799       cm_msg(MERROR, "bm_defragement_event",
07800              "Received fragments with more data (%d) than event size (%d)",
07801              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
07802       return;
07803    }
07804 
07805    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07806           defrag_buffer[i].received, pdata, pevent->data_size);
07807 
07808    defrag_buffer[i].received += pevent->data_size;
07809 
07810    //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
07811    //       defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
07812 
07813    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
07814       /* event complete */
07815       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
07816       free(defrag_buffer[i].pevent);
07817       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07818    }
07819 }
07820 
07821 /**dox***************************************************************/
07822 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07823 
07824 /**dox***************************************************************/
07825                                                                                                                                /** @} *//* end of bmfunctionc */
07826 
07827 /**dox***************************************************************/
07828 /** @addtogroup rpcfunctionc
07829  *
07830  *  @{  */
07831 
07832 /**dox***************************************************************/
07833 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07834 
07835 /********************************************************************\
07836 *                                                                    *
07837 *                         RPC functions                              *
07838 *                                                                    *
07839 \********************************************************************/
07840 
07841 /* globals */
07842 
07843 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
07844 RPC_SERVER_CONNECTION _server_connection;
07845 
07846 static int _lsock;
07847 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
07848 static INT _server_acception_index = 0;
07849 static INT _server_type;
07850 static char _server_name[256];
07851 
07852 static RPC_LIST *rpc_list = NULL;
07853 
07854 int _opt_tcp_size = OPT_TCP_SIZE;
07855 
07856 
07857 /********************************************************************\
07858 *                       conversion functions                         *
07859 \********************************************************************/
07860 
07861 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
07862 {
07863    *convert_flags = 0;
07864 
07865    /* big/little endian conversion */
07866    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
07867         (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
07868                                            && (hw_type & DRI_BIG_ENDIAN)))
07869       *convert_flags |= CF_ENDIAN;
07870 
07871    /* float conversion between IEEE and VAX G */
07872    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
07873       *convert_flags |= CF_VAX2IEEE;
07874 
07875    /* float conversion between VAX G and IEEE */
07876    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
07877       *convert_flags |= CF_IEEE2VAX;
07878 
07879    /* ASCII format */
07880    if (remote_hw_type & DR_ASCII)
07881       *convert_flags |= CF_ASCII;
07882 }
07883 
07884 /********************************************************************/
07885 void rpc_get_convert_flags(INT * convert_flags)
07886 {
07887    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE), _server_connection.remote_hw_type, convert_flags);
07888 }
07889 
07890 /********************************************************************/
07891 void rpc_ieee2vax_float(float *var)
07892 {
07893    unsigned short int lo, hi;
07894 
07895    /* swap hi and lo word */
07896    lo = *((short int *) (var) + 1);
07897    hi = *((short int *) (var));
07898 
07899    /* correct exponent */
07900    if (lo != 0)
07901       lo += 0x100;
07902 
07903    *((short int *) (var) + 1) = hi;
07904    *((short int *) (var)) = lo;
07905 }
07906 
07907 void rpc_vax2ieee_float(float *var)
07908 {
07909    unsigned short int lo, hi;
07910 
07911    /* swap hi and lo word */
07912    lo = *((short int *) (var) + 1);
07913    hi = *((short int *) (var));
07914 
07915    /* correct exponent */
07916    if (hi != 0)
07917       hi -= 0x100;
07918 
07919    *((short int *) (var) + 1) = hi;
07920    *((short int *) (var)) = lo;
07921 
07922 }
07923 
07924 void rpc_vax2ieee_double(double *var)
07925 {
07926    unsigned short int i1, i2, i3, i4;
07927 
07928    /* swap words */
07929    i1 = *((short int *) (var) + 3);
07930    i2 = *((short int *) (var) + 2);
07931    i3 = *((short int *) (var) + 1);
07932    i4 = *((short int *) (var));
07933 
07934    /* correct exponent */
07935    if (i4 != 0)
07936       i4 -= 0x20;
07937 
07938    *((short int *) (var) + 3) = i4;
07939    *((short int *) (var) + 2) = i3;
07940    *((short int *) (var) + 1) = i2;
07941    *((short int *) (var)) = i1;
07942 }
07943 
07944 void rpc_ieee2vax_double(double *var)
07945 {
07946    unsigned short int i1, i2, i3, i4;
07947 
07948    /* swap words */
07949    i1 = *((short int *) (var) + 3);
07950    i2 = *((short int *) (var) + 2);
07951    i3 = *((short int *) (var) + 1);
07952    i4 = *((short int *) (var));
07953 
07954    /* correct exponent */
07955    if (i1 != 0)
07956       i1 += 0x20;
07957 
07958    *((short int *) (var) + 3) = i4;
07959    *((short int *) (var) + 2) = i3;
07960    *((short int *) (var) + 1) = i2;
07961    *((short int *) (var)) = i1;
07962 }
07963 
07964 /********************************************************************/
07965 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
07966 {
07967 
07968    if (convert_flags & CF_ENDIAN) {
07969       if (tid == TID_WORD || tid == TID_SHORT)
07970          WORD_SWAP(data);
07971       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
07972          DWORD_SWAP(data);
07973       if (tid == TID_DOUBLE)
07974          QWORD_SWAP(data);
07975    }
07976 
07977    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
07978        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
07979       if (tid == TID_FLOAT)
07980          rpc_ieee2vax_float((float *) data);
07981       if (tid == TID_DOUBLE)
07982          rpc_ieee2vax_double((double *) data);
07983    }
07984 
07985    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
07986        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
07987       if (tid == TID_FLOAT)
07988          rpc_vax2ieee_float((float *) data);
07989       if (tid == TID_DOUBLE)
07990          rpc_vax2ieee_double((double *) data);
07991    }
07992 }
07993 
07994 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
07995 /********************************************************************\
07996 
07997   Routine: rpc_convert_data
07998 
07999   Purpose: Convert data format between differenct computers
08000 
08001   Input:
08002     void   *data            Pointer to data
08003     INT    tid              Type ID of data, one of TID_xxx
08004     INT    flags            Combination of following flags:
08005                               RPC_IN: data is input parameter
08006                               RPC_OUT: data is output variable
08007                               RPC_FIXARRAY, RPC_VARARRAY: data is array
08008                                 of "size" bytes (see next param.)
08009                               RPC_OUTGOING: data is outgoing
08010     INT    total_size       Size of bytes of data. Used for variable
08011                             length arrays.
08012     INT    convert_flags    Flags for data conversion
08013 
08014   Output:
08015     void   *data            Is converted according to _convert_flag
08016                             value
08017 
08018   Function value:
08019     RPC_SUCCESS             Successful completion
08020 
08021 \********************************************************************/
08022 {
08023    INT i, n, single_size;
08024    char *p;
08025 
08026    /* convert array */
08027    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
08028       single_size = tid_size[tid];
08029       /* don't convert TID_ARRAY & TID_STRUCT */
08030       if (single_size == 0)
08031          return;
08032 
08033       n = total_size / single_size;
08034 
08035       for (i = 0; i < n; i++) {
08036          p = (char *) data + (i * single_size);
08037          rpc_convert_single(p, tid, flags, convert_flags);
08038       }
08039    } else {
08040       rpc_convert_single(data, tid, flags, convert_flags);
08041    }
08042 }
08043 
08044 /********************************************************************\
08045 *                       type ID functions                            *
08046 \********************************************************************/
08047 
08048 INT rpc_tid_size(INT id)
08049 {
08050    if (id >= 0 && id < TID_LAST)
08051       return tid_size[id];
08052 
08053    return 0;
08054 }
08055 
08056 char *rpc_tid_name(INT id)
08057 {
08058    if (id >= 0 && id < TID_LAST)
08059       return tid_name[id];
08060    else
08061       return "<unknown>";
08062 }
08063 
08064 
08065 /**dox***************************************************************/
08066 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08067 
08068 /********************************************************************\
08069 *                        client functions                            *
08070 \********************************************************************/
08071 
08072 /********************************************************************/
08073 /**
08074 Register RPC client for standalone mode (without standard
08075            midas server)
08076 @param list           Array of RPC_LIST structures containing
08077                             function IDs and parameter definitions.
08078                             The end of the list must be indicated by
08079                             a function ID of zero.
08080 @param name          Name of this client
08081 @return RPC_SUCCESS
08082 */
08083 INT rpc_register_client(const char *name, RPC_LIST * list)
08084 {
08085    rpc_set_name(name);
08086    rpc_register_functions(rpc_get_internal_list(0), NULL);
08087    rpc_register_functions(list, NULL);
08088 
08089    return RPC_SUCCESS;
08090 }
08091 
08092 /********************************************************************/
08093 /**
08094 Register a set of RPC functions (both as clients or servers)
08095 @param new_list       Array of RPC_LIST structures containing
08096                             function IDs and parameter definitions.
08097                             The end of the list must be indicated by
08098                             a function ID of zero.
08099 @param func          Default dispatch function
08100 
08101 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
08102 */
08103 INT rpc_register_functions(const RPC_LIST * new_list, INT(*func) (INT, void **))
08104 {
08105    INT i, j, iold, inew;
08106 
08107    /* count number of new functions */
08108    for (i = 0; new_list[i].id != 0; i++) {
08109       /* check double defined functions */
08110       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
08111          if (rpc_list[j].id == new_list[i].id)
08112             return RPC_DOUBLE_DEFINED;
08113    }
08114    inew = i;
08115 
08116    /* count number of existing functions */
08117    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
08118    iold = i;
08119 
08120    /* allocate new memory for rpc_list */
08121    if (rpc_list == NULL)
08122       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
08123    else
08124       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
08125 
08126    if (rpc_list == NULL) {
08127       cm_msg(MERROR, "rpc_register_functions", "out of memory");
08128       return RPC_NO_MEMORY;
08129    }
08130 
08131    /* append new functions */
08132    for (i = iold; i < iold + inew; i++) {
08133       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
08134 
08135       /* set default dispatcher */
08136       if (rpc_list[i].dispatch == NULL)
08137          rpc_list[i].dispatch = func;
08138 
08139       /* check valid ID for user functions */
08140       if (new_list != rpc_get_internal_list(0) &&
08141           new_list != rpc_get_internal_list(1) && (rpc_list[i].id < RPC_MIN_ID
08142                                                    || rpc_list[i].id > RPC_MAX_ID))
08143          cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID");
08144    }
08145 
08146    /* mark end of list */
08147    rpc_list[i].id = 0;
08148 
08149    return RPC_SUCCESS;
08150 }
08151 
08152 
08153 
08154 /**dox***************************************************************/
08155 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08156 
08157 /********************************************************************/
08158 INT rpc_deregister_functions()
08159 /********************************************************************\
08160 
08161   Routine: rpc_deregister_functions
08162 
08163   Purpose: Free memory of previously registered functions
08164 
08165   Input:
08166     none
08167 
08168   Output:
08169     none
08170 
08171   Function value:
08172     RPC_SUCCESS              Successful completion
08173 
08174 \********************************************************************/
08175 {
08176    if (rpc_list)
08177       M_FREE(rpc_list);
08178    rpc_list = NULL;
08179 
08180    return RPC_SUCCESS;
08181 }
08182 
08183 
08184 /********************************************************************/
08185 INT rpc_register_function(INT id, INT(*func) (INT, void **))
08186 /********************************************************************\
08187 
08188   Routine: rpc_register_function
08189 
08190   Purpose: Replace a dispatch function for a specific rpc routine
08191 
08192   Input:
08193     INT      id             RPC ID
08194     INT      *func          New dispatch function
08195 
08196   Output:
08197    <implicit: func gets copied to rpc_list>
08198 
08199   Function value:
08200    RPC_SUCCESS              Successful completion
08201    RPC_INVALID_ID           RPC ID not found
08202 
08203 \********************************************************************/
08204 {
08205    INT i;
08206 
08207    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
08208       if (rpc_list[i].id == id)
08209          break;
08210 
08211    if (rpc_list[i].id == id)
08212       rpc_list[i].dispatch = func;
08213    else
08214       return RPC_INVALID_ID;
08215 
08216    return RPC_SUCCESS;
08217 }
08218 
08219 
08220 /********************************************************************/
08221 INT rpc_client_dispatch(int sock)
08222 /********************************************************************\
08223 
08224   Routine: rpc_client_dispatch
08225 
08226   Purpose: Gets called whenever a client receives data from the
08227            server. Get set via rpc_connect. Internal use only.
08228 
08229 \********************************************************************/
08230 {
08231    INT hDB, hKey, n;
08232    NET_COMMAND *nc;
08233    INT status = 0;
08234    char net_buffer[256];
08235 
08236    nc = (NET_COMMAND *) net_buffer;
08237 
08238    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08239    if (n <= 0)
08240       return SS_ABORT;
08241 
08242    if (nc->header.routine_id == MSG_ODB) {
08243       /* update a changed record */
08244       hDB = *((INT *) nc->param);
08245       hKey = *((INT *) nc->param + 1);
08246       status = db_update_record(hDB, hKey, 0);
08247    }
08248 
08249    else if (nc->header.routine_id == MSG_WATCHDOG) {
08250       nc->header.routine_id = 1;
08251       nc->header.param_size = 0;
08252       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08253       status = RPC_SUCCESS;
08254    }
08255 
08256    else if (nc->header.routine_id == MSG_BM) {
08257       fd_set readfds;
08258       struct timeval timeout;
08259 
08260       /* receive further messages to empty TCP queue */
08261       do {
08262          FD_ZERO(&readfds);
08263          FD_SET(sock, &readfds);
08264 
08265          timeout.tv_sec = 0;
08266          timeout.tv_usec = 0;
08267 
08268          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08269 
08270          if (FD_ISSET(sock, &readfds)) {
08271             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08272             if (n <= 0)
08273                return SS_ABORT;
08274 
08275             if (nc->header.routine_id == MSG_ODB) {
08276                /* update a changed record */
08277                hDB = *((INT *) nc->param);
08278                hKey = *((INT *) nc->param + 1);
08279                status = db_update_record(hDB, hKey, 0);
08280             }
08281 
08282             else if (nc->header.routine_id == MSG_WATCHDOG) {
08283                nc->header.routine_id = 1;
08284                nc->header.param_size = 0;
08285                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08286                status = RPC_SUCCESS;
08287             }
08288          }
08289 
08290       } while (FD_ISSET(sock, &readfds));
08291 
08292       /* poll event from server */
08293       status = bm_poll_event(FALSE);
08294    }
08295 
08296    return status;
08297 }
08298 
08299 
08300 /********************************************************************/
08301 INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE * hConnection)
08302 /********************************************************************\
08303 
08304   Routine: rpc_client_connect
08305 
08306   Purpose: Establish a network connection to a remote client
08307 
08308   Input:
08309     char *host_name          IP address of host to connect to.
08310     INT  port                TPC port to connect to.
08311     char *clinet_name        Client program name
08312 
08313   Output:
08314     HNDLE *hConnection       Handle for new connection which can be used
08315                              in future rpc_call(hConnection....) calls
08316 
08317   Function value:
08318     RPC_SUCCESS              Successful completion
08319     RPC_NET_ERROR            Error in socket call
08320     RPC_NO_CONNECTION        Maximum number of connections reached
08321     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08322 
08323 \********************************************************************/
08324 {
08325    INT i, status, idx;
08326    struct sockaddr_in bind_addr;
08327    INT sock;
08328    INT remote_hw_type, hw_type;
08329    char str[200];
08330    char version[32], v1[32];
08331    char local_prog_name[NAME_LENGTH];
08332    char local_host_name[HOST_NAME_LENGTH];
08333    struct hostent *phe;
08334 
08335 #ifdef OS_WINNT
08336    {
08337       WSADATA WSAData;
08338 
08339       /* Start windows sockets */
08340       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08341          return RPC_NET_ERROR;
08342    }
08343 #endif
08344 
08345    /* check if cm_connect_experiment was called */
08346    if (_client_name[0] == 0) {
08347       cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
08348       return RPC_NOT_REGISTERED;
08349    }
08350 
08351    /* check for broken connections */
08352    rpc_client_check();
08353 
08354    /* check if connection already exists */
08355    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08356       if (_client_connection[i].send_sock != 0 &&
08357           strcmp(_client_connection[i].host_name, host_name) == 0 && _client_connection[i].port == port) {
08358          *hConnection = i + 1;
08359          return RPC_SUCCESS;
08360       }
08361 
08362    /* search for free entry */
08363    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08364       if (_client_connection[i].send_sock == 0)
08365          break;
08366 
08367    /* open new network connection */
08368    if (i == MAX_RPC_CONNECTION) {
08369       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
08370       return RPC_NO_CONNECTION;
08371    }
08372 
08373    /* create a new socket for connecting to remote server */
08374    sock = socket(AF_INET, SOCK_STREAM, 0);
08375    if (sock == -1) {
08376       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
08377       return RPC_NET_ERROR;
08378    }
08379 
08380    idx = i;
08381    strcpy(_client_connection[idx].host_name, host_name);
08382    strcpy(_client_connection[idx].client_name, client_name);
08383    _client_connection[idx].port = port;
08384    _client_connection[idx].exp_name[0] = 0;
08385    _client_connection[idx].transport = RPC_TCP;
08386    _client_connection[idx].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08387    _client_connection[idx].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08388 
08389    /* connect to remote node */
08390    memset(&bind_addr, 0, sizeof(bind_addr));
08391    bind_addr.sin_family = AF_INET;
08392    bind_addr.sin_addr.s_addr = 0;
08393    bind_addr.sin_port = htons((short) port);
08394 
08395 #ifdef OS_VXWORKS
08396    {
08397       INT host_addr;
08398 
08399       host_addr = hostGetByName(host_name);
08400       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08401    }
08402 #else
08403    phe = gethostbyname(host_name);
08404    if (phe == NULL) {
08405       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
08406       return RPC_NET_ERROR;
08407    }
08408    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08409 #endif
08410 
08411 #ifdef OS_UNIX
08412    do {
08413       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
08414 
08415       /* don't return if an alarm signal was cought */
08416    } while (status == -1 && errno == EINTR);
08417 #else
08418    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08419 #endif
08420 
08421    if (status != 0) {
08422       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
08423          message should be displayed by application */
08424       return RPC_NET_ERROR;
08425    }
08426 
08427    /* set TCP_NODELAY option for better performance */
08428    i = 1;
08429    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
08430 
08431    /* send local computer info */
08432    rpc_get_name(local_prog_name);
08433    gethostname(local_host_name, sizeof(local_host_name));
08434 
08435    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08436    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name, local_host_name);
08437 
08438    send(sock, str, strlen(str) + 1, 0);
08439 
08440    /* receive remote computer info */
08441    i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
08442    if (i <= 0) {
08443       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s", str);
08444       return RPC_NET_ERROR;
08445    }
08446 
08447    remote_hw_type = version[0] = 0;
08448    sscanf(str, "%d %s", &remote_hw_type, version);
08449    _client_connection[idx].remote_hw_type = remote_hw_type;
08450    _client_connection[idx].send_sock = sock;
08451 
08452    /* print warning if version patch level doesn't agree */
08453    strcpy(v1, version);
08454    if (strchr(v1, '.'))
08455       if (strchr(strchr(v1, '.') + 1, '.'))
08456          *strchr(strchr(v1, '.') + 1, '.') = 0;
08457 
08458    strcpy(str, cm_get_version());
08459    if (strchr(str, '.'))
08460       if (strchr(strchr(str, '.') + 1, '.'))
08461          *strchr(strchr(str, '.') + 1, '.') = 0;
08462 
08463    if (strcmp(v1, str) != 0) {
08464       sprintf(str, "remote MIDAS version %s differs from local version %s", version, cm_get_version());
08465       cm_msg(MERROR, "rpc_client_connect", str);
08466    }
08467 
08468    *hConnection = idx + 1;
08469 
08470    return RPC_SUCCESS;
08471 }
08472 
08473 /********************************************************************/
08474 void rpc_client_check()
08475 /********************************************************************\
08476 
08477   Routine: rpc_client_check
08478 
08479   Purpose: Check all client connections if remote client closed link
08480 
08481   Function value:
08482     RPC_SUCCESS              Successful completion
08483     RPC_NET_ERROR            Error in socket call
08484     RPC_NO_CONNECTION        Maximum number of connections reached
08485     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08486 
08487 \********************************************************************/
08488 {
08489    INT i, status;
08490 
08491    /* check for broken connections */
08492    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08493       if (_client_connection[i].send_sock != 0) {
08494          int sock;
08495          fd_set readfds;
08496          struct timeval timeout;
08497          char buffer[64];
08498 
08499          sock = _client_connection[i].send_sock;
08500          FD_ZERO(&readfds);
08501          FD_SET(sock, &readfds);
08502 
08503          timeout.tv_sec = 0;
08504          timeout.tv_usec = 0;
08505 
08506          do {
08507             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08508          } while (status == -1);        /* dont return if an alarm signal was cought */
08509 
08510          if (FD_ISSET(sock, &readfds)) {
08511             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
08512 
08513             if (equal_ustring(buffer, "EXIT")) {
08514                /* normal exit */
08515                closesocket(sock);
08516                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08517             }
08518 
08519             if (status <= 0) {
08520                cm_msg(MERROR, "rpc_client_check",
08521                       "Connection broken to \"%s\" on host %s",
08522                       _client_connection[i].client_name, _client_connection[i].host_name);
08523 
08524                /* connection broken -> reset */
08525                closesocket(sock);
08526                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08527             }
08528          }
08529       }
08530 }
08531 
08532 
08533 /********************************************************************/
08534 INT rpc_server_connect(const char *host_name, const char *exp_name)
08535 /********************************************************************\
08536 
08537   Routine: rpc_server_connect
08538 
08539   Purpose: Extablish a network connection to a remote MIDAS
08540            server using a callback scheme.
08541 
08542   Input:
08543     char *host_name         IP address of host to connect to.
08544 
08545     INT  port               TPC port to connect to.
08546 
08547     char *exp_name          Name of experiment to connect to. By using
08548                             this name, several experiments (e.g. online
08549                             DAQ and offline analysis) can run simultan-
08550                             eously on the same host.
08551 
08552   Output:
08553     none
08554 
08555   Function value:
08556     RPC_SUCCESS              Successful completion
08557     RPC_NET_ERROR            Error in socket call
08558     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08559     CM_UNDEF_EXP             Undefined experiment on server
08560 
08561 \********************************************************************/
08562 {
08563    INT i, status, flag;
08564    struct sockaddr_in bind_addr;
08565    INT sock, lsock1, lsock2, lsock3;
08566    INT listen_port1, listen_port2, listen_port3;
08567    INT remote_hw_type, hw_type;
08568    unsigned int size;
08569    char str[200], version[32], v1[32];
08570    char local_prog_name[NAME_LENGTH];
08571    struct hostent *phe;
08572    fd_set readfds;
08573    struct timeval timeout;
08574    int port = MIDAS_TCP_PORT;
08575    char *s;
08576 
08577 #ifdef OS_WINNT
08578    {
08579       WSADATA WSAData;
08580 
08581       /* Start windows sockets */
08582       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08583          return RPC_NET_ERROR;
08584    }
08585 #endif
08586 
08587    /* check if local connection */
08588    if (host_name[0] == 0)
08589       return RPC_SUCCESS;
08590 
08591    /* register system functions */
08592    rpc_register_functions(rpc_get_internal_list(0), NULL);
08593 
08594    /* check if cm_connect_experiment was called */
08595    if (_client_name[0] == 0) {
08596       cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
08597       return RPC_NOT_REGISTERED;
08598    }
08599 
08600    /* check if connection already exists */
08601    if (_server_connection.send_sock != 0)
08602       return RPC_SUCCESS;
08603 
08604    strcpy(_server_connection.host_name, host_name);
08605    strcpy(_server_connection.exp_name, exp_name);
08606    _server_connection.transport = RPC_TCP;
08607    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08608 
08609    /* create new TCP sockets for listening */
08610    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08611    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08612    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08613    if (lsock3 == -1) {
08614       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08615       return RPC_NET_ERROR;
08616    }
08617 
08618    flag = 1;
08619    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08620    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08621    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08622 
08623    /* let OS choose any port number */
08624    memset(&bind_addr, 0, sizeof(bind_addr));
08625    bind_addr.sin_family = AF_INET;
08626    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08627    bind_addr.sin_port = 0;
08628 
08629    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08630    bind_addr.sin_port = 0;
08631    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08632    bind_addr.sin_port = 0;
08633    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08634    if (status < 0) {
08635       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08636       return RPC_NET_ERROR;
08637    }
08638 
08639    /* listen for connection */
08640    status = listen(lsock1, 1);
08641    status = listen(lsock2, 1);
08642    status = listen(lsock3, 1);
08643    if (status < 0) {
08644       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08645       return RPC_NET_ERROR;
08646    }
08647 
08648    /* find out which port OS has chosen */
08649    size = sizeof(bind_addr);
08650 #ifdef OS_WINNT
08651    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08652    listen_port1 = ntohs(bind_addr.sin_port);
08653    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08654    listen_port2 = ntohs(bind_addr.sin_port);
08655    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08656    listen_port3 = ntohs(bind_addr.sin_port);
08657 #else
08658    getsockname(lsock1, (struct sockaddr *) &bind_addr, &size);
08659    listen_port1 = ntohs(bind_addr.sin_port);
08660    getsockname(lsock2, (struct sockaddr *) &bind_addr, &size);
08661    listen_port2 = ntohs(bind_addr.sin_port);
08662    getsockname(lsock3, (struct sockaddr *) &bind_addr, &size);
08663    listen_port3 = ntohs(bind_addr.sin_port);
08664 #endif
08665 
08666    /* create a new socket for connecting to remote server */
08667    sock = socket(AF_INET, SOCK_STREAM, 0);
08668    if (sock == -1) {
08669       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08670       return RPC_NET_ERROR;
08671    }
08672 
08673    /* extract port number from host_name */
08674    strlcpy(str, host_name, sizeof(str));
08675    s = strchr(str, ':');
08676    if (s) {
08677       *s = 0;
08678       port = strtoul(s + 1, NULL, 0);
08679    }
08680 
08681    /* connect to remote node */
08682    memset(&bind_addr, 0, sizeof(bind_addr));
08683    bind_addr.sin_family = AF_INET;
08684    bind_addr.sin_addr.s_addr = 0;
08685    bind_addr.sin_port = htons(port);
08686 
08687 #ifdef OS_VXWORKS
08688    {
08689       INT host_addr;
08690 
08691       host_addr = hostGetByName(str);
08692       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08693    }
08694 #else
08695    phe = gethostbyname(str);
08696    if (phe == NULL) {
08697       cm_msg(MERROR, "rpc_server_connect", "cannot resolve host name \'%s\'", str);
08698       return RPC_NET_ERROR;
08699    }
08700    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08701 #endif
08702 
08703 #ifdef OS_UNIX
08704    do {
08705       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08706 
08707       /* don't return if an alarm signal was cought */
08708    } while (status == -1 && errno == EINTR);
08709 #else
08710    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08711 #endif
08712 
08713    if (status != 0) {
08714 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08715       return RPC_NET_ERROR;
08716    }
08717 
08718    /* connect to experiment */
08719    if (exp_name[0] == 0)
08720       sprintf(str, "C %d %d %d %s Default", listen_port1, listen_port2, listen_port3, cm_get_version());
08721    else
08722       sprintf(str, "C %d %d %d %s %s", listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08723 
08724    send(sock, str, strlen(str) + 1, 0);
08725    i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
08726    closesocket(sock);
08727    if (i <= 0) {
08728       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08729       return RPC_NET_ERROR;
08730    }
08731 
08732    status = version[0] = 0;
08733    sscanf(str, "%d %s", &status, version);
08734 
08735    if (status == 2) {
08736 /*  message "undefined experiment" should be displayed by application */
08737       return CM_UNDEF_EXP;
08738    }
08739 
08740    /* print warning if version patch level doesn't agree */
08741    strcpy(v1, version);
08742    if (strchr(v1, '.'))
08743       if (strchr(strchr(v1, '.') + 1, '.'))
08744          *strchr(strchr(v1, '.') + 1, '.') = 0;
08745 
08746    strcpy(str, cm_get_version());
08747    if (strchr(str, '.'))
08748       if (strchr(strchr(str, '.') + 1, '.'))
08749          *strchr(strchr(str, '.') + 1, '.') = 0;
08750 
08751    if (strcmp(v1, str) != 0) {
08752       sprintf(str, "remote MIDAS version %s differs from local version %s", version, cm_get_version());
08753       cm_msg(MERROR, "rpc_server_connect", str);
08754    }
08755 
08756    /* wait for callback on send and recv socket with timeout */
08757    FD_ZERO(&readfds);
08758    FD_SET(lsock1, &readfds);
08759    FD_SET(lsock2, &readfds);
08760    FD_SET(lsock3, &readfds);
08761 
08762    timeout.tv_sec = _rpc_connect_timeout / 1000;
08763    timeout.tv_usec = 0;
08764 
08765    do {
08766       status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08767 
08768       /* if an alarm signal was cought, restart select with reduced timeout */
08769       if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
08770          timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
08771 
08772    } while (status == -1);      /* dont return if an alarm signal was cought */
08773 
08774    if (!FD_ISSET(lsock1, &readfds)) {
08775       cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
08776       closesocket(lsock1);
08777       closesocket(lsock2);
08778       closesocket(lsock3);
08779       return RPC_NET_ERROR;
08780    }
08781 
08782    size = sizeof(bind_addr);
08783 
08784 #ifdef OS_WINNT
08785    _server_connection.send_sock = accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08786    _server_connection.recv_sock = accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08787    _server_connection.event_sock = accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08788 #else
08789    _server_connection.send_sock = accept(lsock1, (struct sockaddr *) &bind_addr, &size);
08790    _server_connection.recv_sock = accept(lsock2, (struct sockaddr *) &bind_addr, &size);
08791    _server_connection.event_sock = accept(lsock3, (struct sockaddr *) &bind_addr, &size);
08792 #endif
08793 
08794    if (_server_connection.send_sock == -1 || _server_connection.recv_sock == -1
08795        || _server_connection.event_sock == -1) {
08796       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08797       return RPC_NET_ERROR;
08798    }
08799 
08800    closesocket(lsock1);
08801    closesocket(lsock2);
08802    closesocket(lsock3);
08803 
08804    /* set TCP_NODELAY option for better performance */
08805    flag = 1;
08806    setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08807    setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08808 
08809    /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
08810    flag = 2 * 1024 * 1024;
08811    status = setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
08812    if (status != 0)
08813       cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno,
08814              strerror(errno));
08815 
08816    /* send local computer info */
08817    rpc_get_name(local_prog_name);
08818    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08819    sprintf(str, "%d %s", hw_type, local_prog_name);
08820 
08821    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08822 
08823    /* receive remote computer info */
08824    i = recv_string(_server_connection.send_sock, str, sizeof(str), _rpc_connect_timeout);
08825    if (i <= 0) {
08826       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
08827       return RPC_NET_ERROR;
08828    }
08829 
08830    sscanf(str, "%d", &remote_hw_type);
08831    _server_connection.remote_hw_type = remote_hw_type;
08832 
08833    /* set dispatcher which receives database updates */
08834    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection, (int (*)(void)) rpc_client_dispatch);
08835 
08836    return RPC_SUCCESS;
08837 }
08838 
08839 /********************************************************************/
08840 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08841 /********************************************************************\
08842 
08843   Routine: rpc_client_disconnect
08844 
08845   Purpose: Close a rpc connection to a MIDAS client
08846 
08847   Input:
08848     HNDLE  hConn           Handle of connection
08849     BOOL   bShutdown       Shut down remote server if TRUE
08850 
08851   Output:
08852     none
08853 
08854   Function value:
08855    RPC_SUCCESS             Successful completion
08856 
08857 \********************************************************************/
08858 {
08859    INT i;
08860 
08861    if (hConn == -1) {
08862       /* close all open connections */
08863       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
08864          if (_client_connection[i].send_sock != 0)
08865             rpc_client_disconnect(i + 1, FALSE);
08866 
08867       /* close server connection from other clients */
08868       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08869          if (_server_acception[i].recv_sock) {
08870             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
08871             closesocket(_server_acception[i].recv_sock);
08872          }
08873    } else {
08874       /* notify server about exit */
08875 
08876       /* set FTCP mode (helps for rebooted VxWorks nodes) */
08877       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
08878       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
08879 
08880       /* close socket */
08881       if (_client_connection[hConn - 1].send_sock)
08882          closesocket(_client_connection[hConn - 1].send_sock);
08883 
08884       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
08885    }
08886 
08887    return RPC_SUCCESS;
08888 }
08889 
08890 
08891 /********************************************************************/
08892 INT rpc_server_disconnect()
08893 /********************************************************************\
08894 
08895   Routine: rpc_server_disconnect
08896 
08897   Purpose: Close a rpc connection to a MIDAS server and close all
08898            server connections from other clients
08899 
08900   Input:
08901     none
08902 
08903   Output:
08904     none
08905 
08906   Function value:
08907    RPC_SUCCESS             Successful completion
08908    RPC_NET_ERROR           Error in socket call
08909    RPC_NO_CONNECTION       Maximum number of connections reached
08910 
08911 \********************************************************************/
08912 {
08913    static int rpc_server_disconnect_recursion_level = 0;
08914 
08915    if (rpc_server_disconnect_recursion_level)
08916       return RPC_SUCCESS;
08917 
08918    rpc_server_disconnect_recursion_level = 1;
08919 
08920    /* flush remaining events */
08921    rpc_flush_event();
08922 
08923    /* notify server about exit */
08924    rpc_call(RPC_ID_EXIT);
08925 
08926    /* close sockets */
08927    closesocket(_server_connection.send_sock);
08928    closesocket(_server_connection.recv_sock);
08929    closesocket(_server_connection.event_sock);
08930 
08931    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
08932 
08933    /* remove semaphore */
08934    if (_mutex_rpc)
08935       ss_mutex_delete(_mutex_rpc);
08936 
08937    rpc_server_disconnect_recursion_level = 0;
08938    return RPC_SUCCESS;
08939 }
08940 
08941 
08942 /********************************************************************/
08943 INT rpc_is_remote(void)
08944 /********************************************************************\
08945 
08946   Routine: rpc_is_remote
08947 
08948   Purpose: Return true if program is connected to a remote server
08949 
08950   Input:
08951    none
08952 
08953   Output:
08954     none
08955 
08956   Function value:
08957     INT    RPC connection index
08958 
08959 \********************************************************************/
08960 {
08961    return _server_connection.send_sock != 0;
08962 }
08963 
08964 
08965 /********************************************************************/
08966 INT rpc_get_server_acception(void)
08967 /********************************************************************\
08968 
08969   Routine: rpc_get_server_acception
08970 
08971   Purpose: Return actual RPC server connection index
08972 
08973   Input:
08974    none
08975 
08976   Output:
08977     none
08978 
08979   Function value:
08980     INT    RPC server connection index
08981 
08982 \********************************************************************/
08983 {
08984    return _server_acception_index;
08985 }
08986 
08987 
08988 /********************************************************************/
08989 INT rpc_set_server_acception(INT idx)
08990 /********************************************************************\
08991 
08992   Routine: rpc_set_server_acception
08993 
08994   Purpose: Set actual RPC server connection index
08995 
08996   Input:
08997     INT  idx                Server index
08998 
08999   Output:
09000     none
09001 
09002   Function value:
09003     RPC_SUCCESS             Successful completion
09004 
09005 \********************************************************************/
09006 {
09007    _server_acception_index = idx;
09008    return RPC_SUCCESS;
09009 }
09010 
09011 
09012 /********************************************************************/
09013 INT rpc_get_option(HNDLE hConn, INT item)
09014 /********************************************************************\
09015 
09016   Routine: rpc_get_option
09017 
09018   Purpose: Get actual RPC option
09019 
09020   Input:
09021     HNDLE hConn             RPC connection handle, -1 for server connection, -2 for rpc connect timeout
09022 
09023     INT   item              One of RPC_Oxxx
09024 
09025   Output:
09026     none
09027 
09028   Function value:
09029     INT                     Actual option
09030 
09031 \********************************************************************/
09032 {
09033    switch (item) {
09034    case RPC_OTIMEOUT:
09035       if (hConn == -1)
09036          return _server_connection.rpc_timeout;
09037       if (hConn == -2)
09038          return _rpc_connect_timeout;
09039       return _client_connection[hConn - 1].rpc_timeout;
09040 
09041    case RPC_OTRANSPORT:
09042       if (hConn == -1)
09043          return _server_connection.transport;
09044       return _client_connection[hConn - 1].transport;
09045 
09046    case RPC_OHW_TYPE:
09047       {
09048          INT tmp_type, size;
09049          DWORD dummy;
09050          unsigned char *p;
09051          float f;
09052          double d;
09053 
09054          tmp_type = 0;
09055 
09056          /* test pointer size */
09057          size = sizeof(p);
09058          if (size == 2)
09059             tmp_type |= DRI_16;
09060          if (size == 4)
09061             tmp_type |= DRI_32;
09062          if (size == 8)
09063             tmp_type |= DRI_64;
09064 
09065          /* test if little or big endian machine */
09066          dummy = 0x12345678;
09067          p = (unsigned char *) &dummy;
09068          if (*p == 0x78)
09069             tmp_type |= DRI_LITTLE_ENDIAN;
09070          else if (*p == 0x12)
09071             tmp_type |= DRI_BIG_ENDIAN;
09072          else
09073             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
09074 
09075          /* floating point format */
09076          f = (float) 1.2345;
09077          dummy = 0;
09078          memcpy(&dummy, &f, sizeof(f));
09079          if ((dummy & 0xFF) == 0x19 &&
09080              ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
09081              && ((dummy >> 24) & 0xFF) == 0x3F)
09082             tmp_type |= DRF_IEEE;
09083          else if ((dummy & 0xFF) == 0x9E &&
09084                   ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
09085                   && ((dummy >> 24) & 0xFF) == 0x04)
09086             tmp_type |= DRF_G_FLOAT;
09087          else
09088             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09089 
09090          d = (double) 1.2345;
09091          dummy = 0;
09092          memcpy(&dummy, &d, sizeof(f));
09093          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
09094              ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
09095              && ((dummy >> 24) & 0xFF) == 0x12)
09096             tmp_type |= DRF_IEEE;
09097          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
09098                   ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
09099                   && ((dummy >> 24) & 0xFF) == 0x3F)
09100             tmp_type |= DRF_IEEE;
09101          else if ((dummy & 0xFF) == 0x13 &&
09102                   ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
09103                   && ((dummy >> 24) & 0xFF) == 0xC0)
09104             tmp_type |= DRF_G_FLOAT;
09105          else if ((dummy & 0xFF) == 0x9E &&
09106                   ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
09107                   && ((dummy >> 24) & 0xFF) == 0x04)
09108             cm_msg(MERROR, "rpc_get_option",
09109                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
09110          else
09111             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09112 
09113          return tmp_type;
09114       }
09115 
09116    default:
09117       cm_msg(MERROR, "rpc_get_option", "invalid argument");
09118       break;
09119    }
09120 
09121    return 0;
09122 }
09123 
09124 /**dox***************************************************************/
09125 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09126 
09127 /********************************************************************/
09128 /**
09129 Set RPC option
09130 @param hConn              RPC connection handle, -1 for server connection, -2 for rpc connect timeout
09131 @param item               One of RPC_Oxxx
09132 @param value              Value to set
09133 @return RPC_SUCCESS
09134 */
09135 INT rpc_set_option(HNDLE hConn, INT item, INT value)
09136 {
09137    switch (item) {
09138    case RPC_OTIMEOUT:
09139       if (hConn == -1)
09140          _server_connection.rpc_timeout = value;
09141       else if (hConn == -2)
09142          _rpc_connect_timeout = value;
09143       else
09144          _client_connection[hConn - 1].rpc_timeout = value;
09145       break;
09146 
09147    case RPC_OTRANSPORT:
09148       if (hConn == -1)
09149          _server_connection.transport = value;
09150       else
09151          _client_connection[hConn - 1].transport = value;
09152       break;
09153 
09154    case RPC_NODELAY:
09155       if (hConn == -1)
09156          setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
09157       else
09158          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value,
09159                     sizeof(value));
09160       break;
09161 
09162    default:
09163       cm_msg(MERROR, "rpc_set_option", "invalid argument");
09164       break;
09165    }
09166 
09167    return 0;
09168 }
09169 
09170 
09171 /**dox***************************************************************/
09172 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09173 
09174 /********************************************************************/
09175 POINTER_T rpc_get_server_option(INT item)
09176 /********************************************************************\
09177 
09178   Routine: rpc_get_server_option
09179 
09180   Purpose: Get actual RPC option for server connection
09181 
09182   Input:
09183     INT  item               One of RPC_Oxxx
09184 
09185   Output:
09186     none
09187 
09188   Function value:
09189     INT                     Actual option
09190 
09191 \********************************************************************/
09192 {
09193    INT i;
09194 
09195    if (item == RPC_OSERVER_TYPE)
09196       return _server_type;
09197 
09198    if (item == RPC_OSERVER_NAME)
09199       return (POINTER_T) _server_name;
09200 
09201    /* return 0 for local calls */
09202    if (_server_type == ST_NONE)
09203       return 0;
09204 
09205    /* check which connections belongs to caller */
09206    if (_server_type == ST_MTHREAD) {
09207       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09208          if (_server_acception[i].tid == ss_gettid())
09209             break;
09210    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09211       i = MAX(0, _server_acception_index - 1);
09212    else
09213       i = 0;
09214 
09215    switch (item) {
09216    case RPC_CONVERT_FLAGS:
09217       return _server_acception[i].convert_flags;
09218    case RPC_ODB_HANDLE:
09219       return _server_acception[i].odb_handle;
09220    case RPC_CLIENT_HANDLE:
09221       return _server_acception[i].client_handle;
09222    case RPC_SEND_SOCK:
09223       return _server_acception[i].send_sock;
09224    case RPC_WATCHDOG_TIMEOUT:
09225       return _server_acception[i].watchdog_timeout;
09226    }
09227 
09228    return 0;
09229 }
09230 
09231 
09232 /********************************************************************/
09233 INT rpc_set_server_option(INT item, POINTER_T value)
09234 /********************************************************************\
09235 
09236   Routine: rpc_set_server_option
09237 
09238   Purpose: Set RPC option for server connection
09239 
09240   Input:
09241    INT  item               One of RPC_Oxxx
09242    INT  value              Value to set
09243 
09244   Output:
09245     none
09246 
09247   Function value:
09248     RPC_SUCCESS             Successful completion
09249 
09250 \********************************************************************/
09251 {
09252    INT i;
09253 
09254    if (item == RPC_OSERVER_TYPE) {
09255       _server_type = value;
09256       return RPC_SUCCESS;
09257    }
09258    if (item == RPC_OSERVER_NAME) {
09259       strcpy(_server_name, (char *) value);
09260       return RPC_SUCCESS;
09261    }
09262 
09263    /* check which connections belongs to caller */
09264    if (_server_type == ST_MTHREAD) {
09265       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09266          if (_server_acception[i].tid == ss_gettid())
09267             break;
09268    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09269       i = MAX(0, _server_acception_index - 1);
09270    else
09271       i = 0;
09272 
09273    switch (item) {
09274    case RPC_CONVERT_FLAGS:
09275       _server_acception[i].convert_flags = value;
09276       break;
09277    case RPC_ODB_HANDLE:
09278       _server_acception[i].odb_handle = value;
09279       break;
09280    case RPC_CLIENT_HANDLE:
09281       _server_acception[i].client_handle = value;
09282       break;
09283    case RPC_WATCHDOG_TIMEOUT:
09284       _server_acception[i].watchdog_timeout = value;
09285       break;
09286    }
09287 
09288    return RPC_SUCCESS;
09289 }
09290 
09291 
09292 /********************************************************************/
09293 INT rpc_get_name(char *name)
09294 /********************************************************************\
09295 
09296   Routine: rpc_get_name
09297 
09298   Purpose: Get name set by rpc_set_name
09299 
09300   Input:
09301     none
09302 
09303   Output:
09304     char*  name             The location pointed by *name receives a
09305                             copy of the _prog_name
09306 
09307   Function value:
09308     RPC_SUCCESS             Successful completion
09309 
09310 \********************************************************************/
09311 {
09312    strcpy(name, _client_name);
09313 
09314    return RPC_SUCCESS;
09315 }
09316 
09317 
09318 /********************************************************************/
09319 INT rpc_set_name(const char *name)
09320 /********************************************************************\
09321 
09322   Routine: rpc_set_name
09323 
09324   Purpose: Set name of actual program for further rpc connections
09325 
09326   Input:
09327    char *name               Program name, up to NAME_LENGTH chars,
09328                             no blanks
09329 
09330   Output:
09331     none
09332 
09333   Function value:
09334     RPC_SUCCESS             Successful completion
09335 
09336 \********************************************************************/
09337 {
09338    strcpy(_client_name, name);
09339 
09340    return RPC_SUCCESS;
09341 }
09342 
09343 
09344 /********************************************************************/
09345 INT rpc_set_debug(void (*func) (char *), INT mode)
09346 /********************************************************************\
09347 
09348   Routine: rpc_set_debug
09349 
09350   Purpose: Set a function which is called on every RPC call to
09351            display the function name and parameters of the RPC
09352            call.
09353 
09354   Input:
09355    void *func(char*)        Pointer to function.
09356    INT  mode                Debug mode
09357 
09358   Output:
09359     none
09360 
09361   Function value:
09362     RPC_SUCCESS             Successful completion
09363 
09364 \********************************************************************/
09365 {
09366    _debug_print = func;
09367    _debug_mode = mode;
09368    return RPC_SUCCESS;
09369 }
09370 
09371 /********************************************************************/
09372 void rpc_debug_printf(const char *format, ...)
09373 /********************************************************************\
09374 
09375   Routine: rpc_debug_print
09376 
09377   Purpose: Calls function set via rpc_set_debug to output a string.
09378 
09379   Input:
09380    char *str                Debug string
09381 
09382   Output:
09383     none
09384 
09385 \********************************************************************/
09386 {
09387    va_list argptr;
09388    char str[1000];
09389 
09390    if (_debug_mode) {
09391       va_start(argptr, format);
09392       vsprintf(str, (char *) format, argptr);
09393       va_end(argptr);
09394 
09395       if (_debug_print) {
09396          strcat(str, "\n");
09397          _debug_print(str);
09398       } else
09399          puts(str);
09400    }
09401 }
09402 
09403 /********************************************************************/
09404 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
09405 {
09406    switch (arg_type) {
09407       /* On the stack, the minimum parameter size is sizeof(int).
09408          To avoid problems on little endian systems, treat all
09409          smaller parameters as int's */
09410    case TID_BYTE:
09411    case TID_SBYTE:
09412    case TID_CHAR:
09413    case TID_WORD:
09414    case TID_SHORT:
09415       *((int *) arg) = va_arg(*arg_ptr, int);
09416       break;
09417 
09418    case TID_INT:
09419    case TID_BOOL:
09420       *((INT *) arg) = va_arg(*arg_ptr, INT);
09421       break;
09422 
09423    case TID_DWORD:
09424       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
09425       break;
09426 
09427       /* float variables are passed as double by the compiler */
09428    case TID_FLOAT:
09429       *((float *) arg) = (float) va_arg(*arg_ptr, double);
09430       break;
09431 
09432    case TID_DOUBLE:
09433       *((double *) arg) = va_arg(*arg_ptr, double);
09434       break;
09435 
09436    case TID_ARRAY:
09437       *((char **) arg) = va_arg(*arg_ptr, char *);
09438       break;
09439    }
09440 }
09441 
09442 /********************************************************************/
09443 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
09444 /********************************************************************\
09445 
09446   Routine: rpc_client_call
09447 
09448   Purpose: Call a function on a MIDAS client
09449 
09450   Input:
09451     INT  hConn              Client connection
09452     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09453 
09454     ...                     variable argument list
09455 
09456   Output:
09457     (depends on argument list)
09458 
09459   Function value:
09460     RPC_SUCCESS             Successful completion
09461     RPC_NET_ERROR           Error in socket call
09462     RPC_NO_CONNECTION       No active connection
09463     RPC_TIMEOUT             Timeout in RPC call
09464     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09465     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09466 
09467 \********************************************************************/
09468 {
09469    va_list ap, aptmp;
09470    char arg[8], arg_tmp[8];
09471    INT arg_type, transport, rpc_timeout;
09472    INT i, idx, status, rpc_index;
09473    INT param_size, arg_size, send_size;
09474    INT tid, flags;
09475    fd_set readfds;
09476    struct timeval timeout;
09477    char *param_ptr, str[80];
09478    BOOL bpointer, bbig;
09479    NET_COMMAND *nc;
09480    int send_sock;
09481    time_t start_time;
09482 
09483    idx = hConn - 1;
09484 
09485    if (_client_connection[idx].send_sock == 0) {
09486       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
09487       return RPC_NO_CONNECTION;
09488    }
09489 
09490    send_sock = _client_connection[idx].send_sock;
09491    rpc_timeout = _client_connection[idx].rpc_timeout;
09492    transport = _client_connection[idx].transport;
09493 
09494    status = resize_net_send_buffer("rpc_client_call", NET_BUFFER_SIZE);
09495    if (status != SUCCESS)
09496       return RPC_EXCEED_BUFFER;
09497 
09498    nc = (NET_COMMAND *) _net_send_buffer;
09499    nc->header.routine_id = routine_id;
09500 
09501    if (transport == RPC_FTCP)
09502       nc->header.routine_id |= TCP_FAST;
09503 
09504    for (i = 0;; i++)
09505       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09506          break;
09507    rpc_index = i;
09508    if (rpc_list[i].id == 0) {
09509       sprintf(str, "invalid rpc ID (%d)", routine_id);
09510       cm_msg(MERROR, "rpc_client_call", str);
09511       return RPC_INVALID_ID;
09512    }
09513 
09514    /* examine variable argument list and convert it to parameter array */
09515    va_start(ap, routine_id);
09516 
09517    /* find out if we are on a big endian system */
09518    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09519 
09520    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09521       tid = rpc_list[rpc_index].param[i].tid;
09522       flags = rpc_list[rpc_index].param[i].flags;
09523 
09524       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09525           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09526           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09527 
09528       if (bpointer)
09529          arg_type = TID_ARRAY;
09530       else
09531          arg_type = tid;
09532 
09533       /* floats are passed as doubles, at least under NT */
09534       if (tid == TID_FLOAT && !bpointer)
09535          arg_type = TID_DOUBLE;
09536 
09537       /* get pointer to argument */
09538       rpc_va_arg(&ap, arg_type, arg);
09539 
09540       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09541       if (bbig) {
09542          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09543             arg[0] = arg[3];
09544          }
09545          if (tid == TID_WORD || tid == TID_SHORT) {
09546             arg[0] = arg[2];
09547             arg[1] = arg[3];
09548          }
09549       }
09550 
09551       if (flags & RPC_IN) {
09552          if (bpointer)
09553             arg_size = tid_size[tid];
09554          else
09555             arg_size = tid_size[arg_type];
09556 
09557          /* for strings, the argument size depends on the string length */
09558          if (tid == TID_STRING || tid == TID_LINK)
09559             arg_size = 1 + strlen((char *) *((char **) arg));
09560 
09561          /* for varibale length arrays, the size is given by
09562             the next parameter on the stack */
09563          if (flags & RPC_VARARRAY) {
09564             memcpy(&aptmp, &ap, sizeof(ap));
09565             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09566 
09567             if (flags & RPC_OUT)
09568                arg_size = *((INT *) * ((void **) arg_tmp));
09569             else
09570                arg_size = *((INT *) arg_tmp);
09571 
09572             *((INT *) param_ptr) = ALIGN8(arg_size);
09573             param_ptr += ALIGN8(sizeof(INT));
09574          }
09575 
09576          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09577             arg_size = rpc_list[rpc_index].param[i].n;
09578 
09579          /* always align parameter size */
09580          param_size = ALIGN8(arg_size);
09581 
09582          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > _net_send_buffer_size) {
09583             cm_msg(MERROR, "rpc_client_call",
09584                    "parameters (%d) too large for network buffer (%d)",
09585                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, _net_send_buffer_size);
09586             return RPC_EXCEED_BUFFER;
09587          }
09588 
09589          if (bpointer)
09590             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09591          else {
09592             /* floats are passed as doubles on most systems */
09593             if (tid != TID_FLOAT)
09594                memcpy(param_ptr, arg, arg_size);
09595             else
09596                *((float *) param_ptr) = (float) *((double *) arg);
09597          }
09598 
09599          param_ptr += param_size;
09600 
09601       }
09602    }
09603 
09604    va_end(ap);
09605 
09606    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09607 
09608    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09609 
09610    /* in FAST TCP mode, only send call and return immediately */
09611    if (transport == RPC_FTCP) {
09612       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09613 
09614       if (i != send_size) {
09615          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09616          return RPC_NET_ERROR;
09617       }
09618 
09619       return RPC_SUCCESS;
09620    }
09621 
09622    /* in TCP mode, send and wait for reply on send socket */
09623    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09624    if (i != send_size) {
09625       cm_msg(MERROR, "rpc_client_call",
09626              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09627              rpc_list[rpc_index].name, _client_connection[idx].host_name);
09628       return RPC_NET_ERROR;
09629    }
09630 
09631    /* make some timeout checking */
09632    if (rpc_timeout > 0) {
09633       start_time = ss_time();
09634 
09635       do {
09636          FD_ZERO(&readfds);
09637          FD_SET(send_sock, &readfds);
09638 
09639          timeout.tv_sec = 1;
09640          timeout.tv_usec = 0;
09641 
09642          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09643 
09644          if (status >= 0 && FD_ISSET(send_sock, &readfds))
09645             break;
09646 
09647          if (ss_time() - start_time > rpc_timeout / 1000)
09648             break;
09649 
09650       } while (status == -1 || status == 0);    /* continue again if signal was cought */
09651 
09652       if (!FD_ISSET(send_sock, &readfds)) {
09653          cm_msg(MERROR, "rpc_client_call",
09654                 "rpc timeout after %d sec, routine = \"%s\", host = \"%s\", connection closed",
09655                 (int) (ss_time() - start_time), rpc_list[rpc_index].name, _client_connection[idx].host_name);
09656 
09657          /* disconnect to avoid that the reply to this rpc_call comes at
09658             the next rpc_call */
09659          rpc_client_disconnect(hConn, FALSE);
09660 
09661          return RPC_TIMEOUT;
09662       }
09663    }
09664 
09665    /* receive result on send socket */
09666    i = recv_tcp(send_sock, _net_send_buffer, _net_send_buffer_size, 0);
09667 
09668    if (i <= 0) {
09669       cm_msg(MERROR, "rpc_client_call",
09670              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09671              rpc_list[rpc_index].name, _client_connection[idx].host_name);
09672       return RPC_NET_ERROR;
09673    }
09674 
09675    /* extract result variables and place it to argument list */
09676    status = nc->header.routine_id;
09677 
09678    va_start(ap, routine_id);
09679 
09680    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09681       tid = rpc_list[rpc_index].param[i].tid;
09682       flags = rpc_list[rpc_index].param[i].flags;
09683 
09684       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09685           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09686           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09687 
09688       if (bpointer)
09689          arg_type = TID_ARRAY;
09690       else
09691          arg_type = rpc_list[rpc_index].param[i].tid;
09692 
09693       if (tid == TID_FLOAT && !bpointer)
09694          arg_type = TID_DOUBLE;
09695 
09696       rpc_va_arg(&ap, arg_type, arg);
09697 
09698       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09699          tid = rpc_list[rpc_index].param[i].tid;
09700          flags = rpc_list[rpc_index].param[i].flags;
09701 
09702          arg_size = tid_size[tid];
09703 
09704          if (tid == TID_STRING || tid == TID_LINK)
09705             arg_size = strlen((char *) (param_ptr)) + 1;
09706 
09707          if (flags & RPC_VARARRAY) {
09708             arg_size = *((INT *) param_ptr);
09709             param_ptr += ALIGN8(sizeof(INT));
09710          }
09711 
09712          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09713             arg_size = rpc_list[rpc_index].param[i].n;
09714 
09715          /* return parameters are always pointers */
09716          if (*((char **) arg))
09717             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09718 
09719          /* parameter size is always aligned */
09720          param_size = ALIGN8(arg_size);
09721 
09722          param_ptr += param_size;
09723       }
09724    }
09725 
09726    va_end(ap);
09727 
09728    return status;
09729 }
09730 
09731 /********************************************************************/
09732 INT rpc_call(const INT routine_id, ...)
09733 /********************************************************************\
09734 
09735   Routine: rpc_call
09736 
09737   Purpose: Call a function on a MIDAS server
09738 
09739   Input:
09740     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09741 
09742     ...                     variable argument list
09743 
09744   Output:
09745     (depends on argument list)
09746 
09747   Function value:
09748     RPC_SUCCESS             Successful completion
09749     RPC_NET_ERROR           Error in socket call
09750     RPC_NO_CONNECTION       No active connection
09751     RPC_TIMEOUT             Timeout in RPC call
09752     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09753     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09754 
09755 \********************************************************************/
09756 {
09757    va_list ap, aptmp;
09758    char arg[8], arg_tmp[8];
09759    INT arg_type, transport, rpc_timeout;
09760    INT i, idx, status;
09761    INT param_size, arg_size, send_size;
09762    INT tid, flags;
09763    fd_set readfds;
09764    struct timeval timeout;
09765    char *param_ptr, str[80];
09766    BOOL bpointer, bbig;
09767    NET_COMMAND *nc;
09768    int send_sock;
09769    time_t start_time;
09770 
09771    send_sock = _server_connection.send_sock;
09772    transport = _server_connection.transport;
09773    rpc_timeout = _server_connection.rpc_timeout;
09774 
09775    /* init network buffer */
09776    if (_net_send_buffer_size == 0) {
09777       status = resize_net_send_buffer("rpc_call", NET_BUFFER_SIZE);
09778       if (status != SUCCESS)
09779          return RPC_EXCEED_BUFFER;
09780 
09781       /* create a local mutex for multi-threaded applications */
09782       ss_mutex_create(&_mutex_rpc);
09783    }
09784 
09785    status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
09786    if (status != SS_SUCCESS) {
09787       cm_msg(MERROR, "rpc_call", "Mutex timeout");
09788       return RPC_MUTEX_TIMEOUT;
09789    }
09790 
09791    nc = (NET_COMMAND *) _net_send_buffer;
09792    nc->header.routine_id = routine_id;
09793 
09794    if (transport == RPC_FTCP)
09795       nc->header.routine_id |= TCP_FAST;
09796 
09797    for (i = 0;; i++)
09798       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09799          break;
09800    idx = i;
09801    if (rpc_list[i].id == 0) {
09802       ss_mutex_release(_mutex_rpc);
09803       sprintf(str, "invalid rpc ID (%d)", routine_id);
09804       cm_msg(MERROR, "rpc_call", str);
09805       return RPC_INVALID_ID;
09806    }
09807 
09808    /* examine variable argument list and convert it to parameter array */
09809    va_start(ap, routine_id);
09810 
09811    /* find out if we are on a big endian system */
09812    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09813 
09814    for (i = 0, param_ptr = nc->param; rpc_list[idx].param[i].tid != 0; i++) {
09815       tid = rpc_list[idx].param[i].tid;
09816       flags = rpc_list[idx].param[i].flags;
09817 
09818       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09819           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09820           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09821 
09822       if (bpointer)
09823          arg_type = TID_ARRAY;
09824       else
09825          arg_type = tid;
09826 
09827       /* floats are passed as doubles, at least under NT */
09828       if (tid == TID_FLOAT && !bpointer)
09829          arg_type = TID_DOUBLE;
09830 
09831       /* get pointer to argument */
09832       rpc_va_arg(&ap, arg_type, arg);
09833 
09834       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09835       if (bbig) {
09836          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09837             arg[0] = arg[3];
09838          }
09839          if (tid == TID_WORD || tid == TID_SHORT) {
09840             arg[0] = arg[2];
09841             arg[1] = arg[3];
09842          }
09843       }
09844 
09845       if (flags & RPC_IN) {
09846          if (bpointer)
09847             arg_size = tid_size[tid];
09848          else
09849             arg_size = tid_size[arg_type];
09850 
09851          /* for strings, the argument size depends on the string length */
09852          if (tid == TID_STRING || tid == TID_LINK)
09853             arg_size = 1 + strlen((char *) *((char **) arg));
09854 
09855          /* for varibale length arrays, the size is given by
09856             the next parameter on the stack */
09857          if (flags & RPC_VARARRAY) {
09858             memcpy(&aptmp, &ap, sizeof(ap));
09859             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09860 
09861             if (flags & RPC_OUT)
09862                arg_size = *((INT *) * ((void **) arg_tmp));
09863             else
09864                arg_size = *((INT *) arg_tmp);
09865 
09866             *((INT *) param_ptr) = ALIGN8(arg_size);
09867             param_ptr += ALIGN8(sizeof(INT));
09868          }
09869 
09870          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09871             arg_size = rpc_list[idx].param[i].n;
09872 
09873          /* always align parameter size */
09874          param_size = ALIGN8(arg_size);
09875 
09876          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > _net_send_buffer_size) {
09877             ss_mutex_release(_mutex_rpc);
09878             cm_msg(MERROR, "rpc_call",
09879                    "parameters (%d) too large for network buffer (%d)",
09880                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, _net_send_buffer_size);
09881             return RPC_EXCEED_BUFFER;
09882          }
09883 
09884          if (bpointer)
09885             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09886          else {
09887             /* floats are passed as doubles on most systems */
09888             if (tid != TID_FLOAT)
09889                memcpy(param_ptr, arg, arg_size);
09890             else
09891                *((float *) param_ptr) = (float) *((double *) arg);
09892          }
09893 
09894          param_ptr += param_size;
09895 
09896       }
09897    }
09898 
09899    va_end(ap);
09900 
09901    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09902 
09903    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09904 
09905    /* in FAST TCP mode, only send call and return immediately */
09906    if (transport == RPC_FTCP) {
09907       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09908 
09909       if (i != send_size) {
09910          ss_mutex_release(_mutex_rpc);
09911          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09912          return RPC_NET_ERROR;
09913       }
09914 
09915       ss_mutex_release(_mutex_rpc);
09916       return RPC_SUCCESS;
09917    }
09918 
09919    /* in TCP mode, send and wait for reply on send socket */
09920    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09921    if (i != send_size) {
09922       ss_mutex_release(_mutex_rpc);
09923       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09924       return RPC_NET_ERROR;
09925    }
09926 
09927    /* make some timeout checking */
09928    if (rpc_timeout > 0) {
09929       start_time = ss_time();
09930 
09931       do {
09932          FD_ZERO(&readfds);
09933          FD_SET(send_sock, &readfds);
09934 
09935          timeout.tv_sec = 1;
09936          timeout.tv_usec = 0;
09937 
09938          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09939 
09940          if (FD_ISSET(send_sock, &readfds))
09941             break;
09942 
09943          if (ss_time() - start_time > rpc_timeout / 1000)
09944             break;
09945 
09946       } while (status == -1 || status == 0);    /* continue again if signal was cought */
09947 
09948       if (!FD_ISSET(send_sock, &readfds)) {
09949          ss_mutex_release(_mutex_rpc);
09950          cm_msg(MERROR, "rpc_call", "rpc timeout after %d sec, routine = \"%s\", program abort",
09951                 (int) (ss_time() - start_time), rpc_list[idx].name);
09952          abort();
09953       }
09954    }
09955 
09956    /* receive result on send socket */
09957    i = recv_tcp(send_sock, _net_send_buffer, _net_send_buffer_size, 0);
09958 
09959    if (i <= 0) {
09960       ss_mutex_release(_mutex_rpc);
09961       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"", rpc_list[idx].name);
09962       return RPC_NET_ERROR;
09963    }
09964 
09965    /* extract result variables and place it to argument list */
09966    status = nc->header.routine_id;
09967 
09968    va_start(ap, routine_id);
09969 
09970    for (i = 0, param_ptr = nc->param; rpc_list[idx].param[i].tid != 0; i++) {
09971       tid = rpc_list[idx].param[i].tid;
09972       flags = rpc_list[idx].param[i].flags;
09973 
09974       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09975           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09976           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09977 
09978       if (bpointer)
09979          arg_type = TID_ARRAY;
09980       else
09981          arg_type = rpc_list[idx].param[i].tid;
09982 
09983       if (tid == TID_FLOAT && !bpointer)
09984          arg_type = TID_DOUBLE;
09985 
09986       rpc_va_arg(&ap, arg_type, arg);
09987 
09988       if (rpc_list[idx].param[i].flags & RPC_OUT) {
09989          tid = rpc_list[idx].param[i].tid;
09990          arg_size = tid_size[tid];
09991 
09992          if (tid == TID_STRING || tid == TID_LINK)
09993             arg_size = strlen((char *) (param_ptr)) + 1;
09994 
09995          if (flags & RPC_VARARRAY) {
09996             arg_size = *((INT *) param_ptr);
09997             param_ptr += ALIGN8(sizeof(INT));
09998          }
09999 
10000          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
10001             arg_size = rpc_list[idx].param[i].n;
10002 
10003          /* return parameters are always pointers */
10004          if (*((char **) arg))
10005             memcpy((void *) *((char **) arg), param_ptr, arg_size);
10006 
10007          /* parameter size is always aligned */
10008          param_size = ALIGN8(arg_size);
10009 
10010          param_ptr += param_size;
10011       }
10012    }
10013 
10014    va_end(ap);
10015 
10016    ss_mutex_release(_mutex_rpc);
10017 
10018    return status;
10019 }
10020 
10021 
10022 /********************************************************************/
10023 INT rpc_set_opt_tcp_size(INT tcp_size)
10024 {
10025    INT old;
10026 
10027    old = _opt_tcp_size;
10028    _opt_tcp_size = tcp_size;
10029    return old;
10030 }
10031 
10032 INT rpc_get_opt_tcp_size()
10033 {
10034    return _opt_tcp_size;
10035 }
10036 
10037 /**dox***************************************************************/
10038 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10039 
10040 /********************************************************************/
10041 /**
10042 Fast send_event routine which bypasses the RPC layer and
10043            sends the event directly at the TCP level.
10044 @param buffer_handle      Handle of the buffer to send the event to.
10045                           Must be obtained via bm_open_buffer.
10046 @param source             Address of the event to send. It must have
10047                           a proper event header.
10048 @param buf_size           Size of event in bytes with header.
10049 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
10050                           function returns immediately if it cannot
10051                           send the event over the network. In SYNC
10052                           mode, it waits until the packet is sent
10053                           (blocking).
10054 @param mode               Determines in which mode the event is sent.
10055                           If zero, use RPC socket, if one, use special
10056                           event socket to bypass RPC layer on the
10057                           server side.
10058 
10059 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR,
10060         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER
10061 */
10062 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag, INT mode)
10063 {
10064    INT i;
10065    NET_COMMAND *nc;
10066    unsigned long flag;
10067    BOOL would_block = 0;
10068    DWORD aligned_buf_size;
10069 
10070    aligned_buf_size = ALIGN8(buf_size);
10071    _rpc_sock = mode == 0 ? _server_connection.send_sock : _server_connection.event_sock;
10072 
10073    if ((INT) aligned_buf_size != (INT) (ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER)))) {
10074       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
10075       return BM_INVALID_PARAM;
10076    }
10077 
10078    if (!rpc_is_remote())
10079       return bm_send_event(buffer_handle, source, buf_size, async_flag);
10080 
10081    /* init network buffer */
10082    if (!_tcp_buffer)
10083       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
10084    if (!_tcp_buffer) {
10085       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
10086       return RPC_EXCEED_BUFFER;
10087    }
10088 
10089    /* check if not enough space in TCP buffer */
10090    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) (_opt_tcp_size - _tcp_wp)
10091        && _tcp_wp != _tcp_rp) {
10092       /* set socket to nonblocking IO */
10093       if (async_flag == ASYNC) {
10094          flag = 1;
10095 #ifdef OS_VXWORKS
10096          ioctlsocket(_rpc_sock, FIONBIO, (int) &flag);
10097 #else
10098          ioctlsocket(_rpc_sock, FIONBIO, &flag);
10099 #endif
10100       }
10101 
10102       i = send_tcp(_rpc_sock, _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10103 
10104       if (i < 0)
10105 #ifdef OS_WINNT
10106          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
10107 #else
10108          would_block = (errno == EWOULDBLOCK);
10109 #endif
10110 
10111       /* set socket back to blocking IO */
10112       if (async_flag == ASYNC) {
10113          flag = 0;
10114 #ifdef OS_VXWORKS
10115          ioctlsocket(_rpc_sock, FIONBIO, (int) &flag);
10116 #else
10117          ioctlsocket(_rpc_sock, FIONBIO, &flag);
10118 #endif
10119       }
10120 
10121       /* increment read pointer */
10122       if (i > 0)
10123          _tcp_rp += i;
10124 
10125       /* check if whole buffer is sent */
10126       if (_tcp_rp == _tcp_wp)
10127          _tcp_rp = _tcp_wp = 0;
10128 
10129       if (i < 0 && !would_block) {
10130          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed, return code = %d", i);
10131          return RPC_NET_ERROR;
10132       }
10133 
10134       /* return if buffer is not emptied */
10135       if (_tcp_wp > 0)
10136          return BM_ASYNC_RETURN;
10137    }
10138 
10139    if (mode == 0) {
10140       nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
10141       nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
10142       nc->header.param_size = 4 * 8 + aligned_buf_size;
10143 
10144       /* assemble parameters manually */
10145       *((INT *) (&nc->param[0])) = buffer_handle;
10146       *((INT *) (&nc->param[8])) = buf_size;
10147 
10148       /* send events larger than optimal buffer size directly */
10149       if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
10150          /* send header */
10151          i = send_tcp(_rpc_sock, _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
10152          if (i <= 0) {
10153             cm_msg(MERROR, "rpc_send_event", "send_tcp() failed, return code = %d", i);
10154             return RPC_NET_ERROR;
10155          }
10156 
10157          /* send data */
10158          i = send_tcp(_rpc_sock, (char *) source, aligned_buf_size, 0);
10159          if (i <= 0) {
10160             cm_msg(MERROR, "rpc_send_event", "send_tcp() failed, return code = %d", i);
10161             return RPC_NET_ERROR;
10162          }
10163 
10164          /* send last two parameters */
10165          *((INT *) (&nc->param[0])) = buf_size;
10166          *((INT *) (&nc->param[8])) = 0;
10167          i = send_tcp(_rpc_sock, &nc->param[0], 16, 0);
10168          if (i <= 0) {
10169             cm_msg(MERROR, "rpc_send_event", "send_tcp() failed, return code = %d", i);
10170             return RPC_NET_ERROR;
10171          }
10172       } else {
10173          /* copy event */
10174          memcpy(&nc->param[16], source, buf_size);
10175 
10176          /* last two parameters (buf_size and async_flag */
10177          *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
10178          *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
10179 
10180          _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
10181       }
10182 
10183    } else {
10184 
10185       /* send events larger than optimal buffer size directly */
10186       if (aligned_buf_size + 4 * 8 + sizeof(INT) >= (DWORD) _opt_tcp_size) {
10187          /* send buffer */
10188          i = send_tcp(_rpc_sock, (char *) &buffer_handle, sizeof(INT), 0);
10189          if (i <= 0) {
10190             cm_msg(MERROR, "rpc_send_event", "send_tcp() failed, return code = %d", i);
10191             return RPC_NET_ERROR;
10192          }
10193 
10194          /* send data */
10195          i = send_tcp(_rpc_sock, (char *) source, aligned_buf_size, 0);
10196          if (i <= 0) {
10197             cm_msg(MERROR, "rpc_send_event", "send_tcp() failed, return code = %d", i);
10198             return RPC_NET_ERROR;
10199          }
10200       } else {
10201          /* copy event */
10202          *((INT *) (_tcp_buffer + _tcp_wp)) = buffer_handle;
10203          _tcp_wp += sizeof(INT);
10204          memcpy(_tcp_buffer + _tcp_wp, source, buf_size);
10205 
10206          _tcp_wp += aligned_buf_size;
10207       }
10208    }
10209 
10210    return RPC_SUCCESS;
10211 }
10212 
10213 
10214 /**dox***************************************************************/
10215 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10216 
10217 /********************************************************************/
10218 int rpc_get_send_sock()
10219 /********************************************************************\
10220 
10221   Routine: rpc_get_send_sock
10222 
10223   Purpose: Return send socket to MIDAS server. Used by MFE.C for
10224            optimized event sending.
10225 
10226   Input:
10227     none
10228 
10229   Output:
10230     none
10231 
10232   Function value:
10233     int    socket
10234 
10235 \********************************************************************/
10236 {
10237    return _server_connection.send_sock;
10238 }
10239 
10240 
10241 /********************************************************************/
10242 int rpc_get_event_sock()
10243 /********************************************************************\
10244 
10245   Routine: rpc_get_event_sock
10246 
10247   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
10248            optimized event sending.
10249 
10250   Input:
10251     none
10252 
10253   Output:
10254     none
10255 
10256   Function value:
10257     int    socket
10258 
10259 \********************************************************************/
10260 {
10261    return _server_connection.event_sock;
10262 }
10263 
10264 /**dox***************************************************************/
10265 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10266 
10267 /********************************************************************/
10268 /**
10269 Send event residing in the TCP cache buffer filled by
10270            rpc_send_event. This routine should be called when a
10271            run is stopped.
10272 
10273 @return RPC_SUCCESS, RPC_NET_ERROR
10274 */
10275 INT rpc_flush_event()
10276 {
10277    INT i;
10278 
10279    if (!rpc_is_remote())
10280       return RPC_SUCCESS;
10281 
10282    /* return if rpc_send_event was not called */
10283    if (!_tcp_buffer || _tcp_wp == 0)
10284       return RPC_SUCCESS;
10285 
10286    /* empty TCP buffer */
10287    if (_tcp_wp > 0) {
10288       i = send_tcp(_rpc_sock, _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10289 
10290       if (i != _tcp_wp - _tcp_rp) {
10291          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
10292          return RPC_NET_ERROR;
10293       }
10294    }
10295 
10296    _tcp_rp = _tcp_wp = 0;
10297 
10298    return RPC_SUCCESS;
10299 }
10300 
10301 /**dox***************************************************************/
10302 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10303 
10304 /********************************************************************/
10305 
10306 typedef struct {
10307    int transition;
10308    int run_number;
10309    time_t trans_time;
10310    int sequence_number;
10311 } TR_FIFO;
10312 
10313 static TR_FIFO tr_fifo[10];
10314 static int trf_wp, trf_rp;
10315 
10316 static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
10317 /********************************************************************\
10318 
10319   Routine: rpc_transition_dispatch
10320 
10321   Purpose: Gets called when a transition function was registered and
10322            a transition occured. Internal use only.
10323 
10324   Input:
10325     INT    idx              RPC function ID
10326     void   *prpc_param      RPC parameters
10327 
10328   Output:
10329     none
10330 
10331   Function value:
10332     INT    return value from called user routine
10333 
10334 \********************************************************************/
10335 {
10336    INT status, i;
10337 
10338    /* erase error string */
10339    *(CSTRING(2)) = 0;
10340 
10341    if (idx == RPC_RC_TRANSITION) {
10342       for (i = 0; i < MAX_TRANSITIONS; i++)
10343          if (_trans_table[i].transition == CINT(0) && _trans_table[i].sequence_number == CINT(4))
10344             break;
10345 
10346       /* call registerd function */
10347       if (i < MAX_TRANSITIONS) {
10348          if (_trans_table[i].func)
10349             /* execute callback if defined */
10350             status = _trans_table[i].func(CINT(1), CSTRING(2));
10351          else {
10352             /* store transition in FIFO */
10353             tr_fifo[trf_wp].transition = CINT(0);
10354             tr_fifo[trf_wp].run_number = CINT(1);
10355             tr_fifo[trf_wp].trans_time = time(NULL);
10356             tr_fifo[trf_wp].sequence_number = CINT(4);
10357             trf_wp = (trf_wp + 1) % 10;
10358             status = RPC_SUCCESS;
10359          }
10360       } else
10361          status = RPC_SUCCESS;
10362 
10363    } else {
10364       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
10365       status = RPC_INVALID_ID;
10366    }
10367 
10368    return status;
10369 }
10370 
10371 /********************************************************************/
10372 int cm_query_transition(int *transition, int *run_number, int *trans_time)
10373 /********************************************************************\
10374 
10375   Routine: cm_query_transition
10376 
10377   Purpose: Query system if transition has occured. Normally, one
10378            registers callbacks for transitions via
10379            cm_register_transition. In some environments however,
10380            callbacks are not possible. In that case one spciefies
10381            a NULL pointer as the callback routine and can query
10382            transitions "manually" by calling this functions. A small
10383            FIFO takes care that no transition is lost if this functions
10384            did not get called between some transitions.
10385 
10386   Output:
10387     INT   *transition        Type of transition, one of TR_xxx
10388     INT   *run_nuber         Run number for transition
10389     time_t *trans_time       Time (in UNIX time) of transition
10390 
10391   Function value:
10392     FALSE  No transition occured since last call
10393     TRUE   Transition occured
10394 
10395 \********************************************************************/
10396 {
10397 
10398    if (trf_wp == trf_rp)
10399       return FALSE;
10400 
10401    if (transition)
10402       *transition = tr_fifo[trf_rp].transition;
10403 
10404    if (run_number)
10405       *run_number = tr_fifo[trf_rp].run_number;
10406 
10407    if (trans_time)
10408       *trans_time = (int) tr_fifo[trf_rp].trans_time;
10409 
10410    trf_rp = (trf_rp + 1) % 10;
10411 
10412    return TRUE;
10413 }
10414 
10415 /********************************************************************\
10416 *                        server functions                            *
10417 \********************************************************************/
10418 
10419 #if 0
10420 void debug_dump(unsigned char *p, int size)
10421 {
10422    int i, j;
10423    unsigned char c;
10424 
10425    for (i = 0; i < (size - 1) / 16 + 1; i++) {
10426       printf("%p ", p + i * 16);
10427       for (j = 0; j < 16; j++)
10428          if (i * 16 + j < size)
10429             printf("%02X ", p[i * 16 + j]);
10430          else
10431             printf("   ");
10432       printf(" ");
10433 
10434       for (j = 0; j < 16; j++) {
10435          c = p[i * 16 + j];
10436          if (i * 16 + j < size)
10437             printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
10438       }
10439       printf("\n");
10440    }
10441 
10442    printf("\n");
10443 }
10444 #endif
10445 
10446 /********************************************************************/
10447 INT recv_tcp_server(INT idx, char *buffer, DWORD buffer_size, INT flags, INT * remaining)
10448 /********************************************************************\
10449 
10450   Routine: recv_tcp_server
10451 
10452   Purpose: TCP receive routine with local cache. To speed up network
10453            performance, a 64k buffer is read in at once and split into
10454            several RPC command on successive calls to recv_tcp_server.
10455            Therefore, the number of recv() calls is minimized.
10456 
10457            This routine is ment to be called by the server process.
10458            Clients should call recv_tcp instead.
10459 
10460   Input:
10461     INT   idx                Index of server connection
10462     DWORD buffer_size        Size of the buffer in bytes.
10463     INT   flags              Flags passed to recv()
10464     INT   convert_flags      Convert flags needed for big/little
10465                              endian conversion
10466 
10467   Output:
10468     char  *buffer            Network receive buffer.
10469     INT   *remaining         Remaining data in cache
10470 
10471   Function value:
10472     INT                      Same as recv()
10473 
10474 \********************************************************************/
10475 {
10476    INT size, param_size;
10477    NET_COMMAND *nc;
10478    INT write_ptr, read_ptr, misalign;
10479    char *net_buffer;
10480    INT copied, status;
10481    INT sock;
10482 
10483    sock = _server_acception[idx].recv_sock;
10484 
10485    if (flags & MSG_PEEK) {
10486       status = recv(sock, buffer, buffer_size, flags);
10487       if (status == -1)
10488          cm_msg(MERROR, "recv_tcp_server",
10489                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status, errno, strerror(errno));
10490       return status;
10491    }
10492 
10493    if (!_server_acception[idx].net_buffer) {
10494       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10495          _server_acception[idx].net_buffer_size = NET_TCP_SIZE;
10496       else
10497          _server_acception[idx].net_buffer_size = NET_BUFFER_SIZE;
10498 
10499       _server_acception[idx].net_buffer = (char *) M_MALLOC(_server_acception[idx].net_buffer_size);
10500       _server_acception[idx].write_ptr = 0;
10501       _server_acception[idx].read_ptr = 0;
10502       _server_acception[idx].misalign = 0;
10503    }
10504    if (!_server_acception[idx].net_buffer) {
10505       cm_msg(MERROR, "recv_tcp_server", "Cannot allocate %d bytes for network buffer",
10506              _server_acception[idx].net_buffer_size);
10507       return -1;
10508    }
10509 
10510    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
10511       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10512       return -1;
10513    }
10514 
10515    copied = 0;
10516    param_size = -1;
10517 
10518    write_ptr = _server_acception[idx].write_ptr;
10519    read_ptr = _server_acception[idx].read_ptr;
10520    misalign = _server_acception[idx].misalign;
10521    net_buffer = _server_acception[idx].net_buffer;
10522 
10523    do {
10524       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
10525          if (param_size == -1) {
10526             if (copied > 0) {
10527                /* assemble split header */
10528                memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
10529                nc = (NET_COMMAND *) (buffer);
10530             } else
10531                nc = (NET_COMMAND *) (net_buffer + read_ptr);
10532 
10533             param_size = (INT) nc->header.param_size;
10534 
10535             if (_server_acception[idx].convert_flags)
10536                rpc_convert_single(&param_size, TID_DWORD, 0, _server_acception[idx].convert_flags);
10537          }
10538 
10539          /* check if parameters fit in buffer */
10540          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
10541             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10542             _server_acception[idx].read_ptr = _server_acception[idx].write_ptr = 0;
10543             return -1;
10544          }
10545 
10546          /* check if we have all parameters in buffer */
10547          if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
10548             break;
10549       }
10550 
10551       /* not enough data, so copy partially and get new */
10552       size = write_ptr - read_ptr;
10553 
10554       if (size > 0) {
10555          memcpy(buffer + copied, net_buffer + read_ptr, size);
10556          copied += size;
10557          read_ptr = write_ptr;
10558       }
10559 #ifdef OS_UNIX
10560       do {
10561          write_ptr = recv(sock, net_buffer + misalign, _server_acception[idx].net_buffer_size - 8, flags);
10562 
10563          /* don't return if an alarm signal was cought */
10564       } while (write_ptr == -1 && errno == EINTR);
10565 #else
10566       write_ptr = recv(sock, net_buffer + misalign, _server_acception[idx].net_buffer_size - 8, flags);
10567 #endif
10568 
10569       /* abort if connection broken */
10570       if (write_ptr <= 0) {
10571          if (write_ptr == 0)
10572             cm_msg(MERROR, "recv_tcp_server", "rpc connection from \'%s\' on \'%s\' unexpectedly closed",
10573                    _server_acception[idx].prog_name, _server_acception[idx].host_name);
10574          else
10575             cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)", write_ptr, errno,
10576                    strerror(errno));
10577 
10578          if (remaining)
10579             *remaining = 0;
10580 
10581          return write_ptr;
10582       }
10583 
10584       read_ptr = misalign;
10585       write_ptr += misalign;
10586 
10587       misalign = write_ptr % 8;
10588    } while (TRUE);
10589 
10590    /* copy rest of parameters */
10591    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
10592    memcpy(buffer + copied, net_buffer + read_ptr, size);
10593    read_ptr += size;
10594 
10595    if (remaining) {
10596       /* don't keep rpc_server_receive in an infinite loop */
10597       if (write_ptr - read_ptr < param_size)
10598          *remaining = 0;
10599       else
10600          *remaining = write_ptr - read_ptr;
10601    }
10602 
10603    _server_acception[idx].write_ptr = write_ptr;
10604    _server_acception[idx].read_ptr = read_ptr;
10605    _server_acception[idx].misalign = misalign;
10606 
10607    return size + copied;
10608 }
10609 
10610 
10611 /********************************************************************/
10612 INT recv_tcp_check(int sock)
10613 /********************************************************************\
10614 
10615   Routine: recv_tcp_check
10616 
10617   Purpose: Check if in TCP receive buffer associated with sock is
10618            some data. Called by ss_suspend.
10619 
10620   Input:
10621     INT   sock               TCP receive socket
10622 
10623   Output:
10624     none
10625 
10626   Function value:
10627     INT   count              Number of bytes remaining in TCP buffer
10628 
10629 \********************************************************************/
10630 {
10631    INT idx;
10632 
10633    /* figure out to which connection socket belongs */
10634    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
10635       if (_server_acception[idx].recv_sock == sock)
10636          break;
10637 
10638    return _server_acception[idx].write_ptr - _server_acception[idx].read_ptr;
10639 }
10640 
10641 
10642 /********************************************************************/
10643 INT recv_event_server(INT idx, char *buffer, DWORD buffer_size, INT flags, INT * remaining)
10644 /********************************************************************\
10645 
10646   Routine: recv_event_server
10647 
10648   Purpose: TCP event receive routine with local cache. To speed up
10649            network performance, a 64k buffer is read in at once and
10650            split into several RPC command on successive calls to
10651            recv_event_server. Therefore, the number of recv() calls
10652            is minimized.
10653 
10654            This routine is ment to be called by the server process.
10655            Clients should call recv_tcp instead.
10656 
10657   Input:
10658     INT   idx                Index of server connection
10659     DWORD buffer_size        Size of the buffer in bytes.
10660     INT   flags              Flags passed to recv()
10661     INT   convert_flags      Convert flags needed for big/little
10662                              endian conversion
10663 
10664   Output:
10665     char  *buffer            Network receive buffer.
10666     INT   *remaining         Remaining data in cache
10667 
10668   Function value:
10669     INT                      Same as recv()
10670 
10671 \********************************************************************/
10672 {
10673    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10674    EVENT_HEADER *pevent;
10675    INT write_ptr, read_ptr, misalign;
10676    char *net_buffer;
10677    INT copied, status;
10678    INT sock;
10679    RPC_SERVER_ACCEPTION *psa;
10680 
10681    psa = &_server_acception[idx];
10682    sock = psa->event_sock;
10683 
10684    //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
10685 
10686    if (flags & MSG_PEEK) {
10687       status = recv(sock, buffer, buffer_size, flags);
10688       if (status == -1)
10689          cm_msg(MERROR, "recv_event_server",
10690                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status, errno, strerror(errno));
10691       return status;
10692    }
10693 
10694    if (!psa->ev_net_buffer) {
10695       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10696          psa->net_buffer_size = NET_TCP_SIZE;
10697       else
10698          psa->net_buffer_size = NET_BUFFER_SIZE;
10699 
10700       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10701 
10702       //printf("allocate %p size %d\n", psa->ev_net_buffer, psa->net_buffer_size);
10703 
10704       psa->ev_write_ptr = 0;
10705       psa->ev_read_ptr = 0;
10706       psa->ev_misalign = 0;
10707    }
10708    if (!psa->ev_net_buffer) {
10709       cm_msg(MERROR, "recv_event_server", "Cannot allocate %d bytes for network buffer",
10710              psa->net_buffer_size);
10711       return -1;
10712    }
10713 
10714    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10715 
10716    if ((INT) buffer_size < header_size) {
10717       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10718       return -1;
10719    }
10720 
10721    copied = 0;
10722    event_size = -1;
10723 
10724    write_ptr = psa->ev_write_ptr;
10725    read_ptr = psa->ev_read_ptr;
10726    misalign = psa->ev_misalign;
10727    net_buffer = psa->ev_net_buffer;
10728 
10729    do {
10730       if (write_ptr - read_ptr >= header_size - copied) {
10731          if (event_size == -1) {
10732             if (copied > 0) {
10733                /* assemble split header */
10734                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10735                pbh = (INT *) buffer;
10736             } else
10737                pbh = (INT *) (net_buffer + read_ptr);
10738 
10739             pevent = (EVENT_HEADER *) (pbh + 1);
10740 
10741             event_size = pevent->data_size;
10742             if (psa->convert_flags)
10743                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10744 
10745             aligned_event_size = ALIGN8(event_size);
10746          }
10747 
10748          /* check if data part fits in buffer */
10749          if ((INT) buffer_size < aligned_event_size + header_size) {
10750             cm_msg(MERROR, "recv_event_server", "event size %d too large for network buffer size %d", aligned_event_size + header_size, buffer_size);
10751             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10752             return -1;
10753          }
10754 
10755          /* check if we have whole event in buffer */
10756          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10757             break;
10758       }
10759 
10760       /* not enough data, so copy partially and get new */
10761       size = write_ptr - read_ptr;
10762 
10763       if (size > 0) {
10764          memcpy(buffer + copied, net_buffer + read_ptr, size);
10765          copied += size;
10766          read_ptr = write_ptr;
10767       }
10768 #ifdef OS_UNIX
10769       do {
10770          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10771 
10772          /* don't return if an alarm signal was cought */
10773       } while (write_ptr == -1 && errno == EINTR);
10774 #else
10775       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10776 #endif
10777 
10778       /* abort if connection broken */
10779       if (write_ptr <= 0) {
10780          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)", write_ptr, errno,
10781                 strerror(errno));
10782 
10783          if (remaining)
10784             *remaining = 0;
10785 
10786          return write_ptr;
10787       }
10788 
10789       read_ptr = misalign;
10790       write_ptr += misalign;
10791 
10792       misalign = write_ptr % 8;
10793    } while (TRUE);
10794 
10795    /* copy rest of event */
10796    size = aligned_event_size + header_size - copied;
10797    if (size > 0) {
10798       memcpy(buffer + copied, net_buffer + read_ptr, size);
10799       read_ptr += size;
10800    }
10801 
10802    if (remaining)
10803       *remaining = write_ptr - read_ptr;
10804 
10805    psa->ev_write_ptr = write_ptr;
10806    psa->ev_read_ptr = read_ptr;
10807    psa->ev_misalign = misalign;
10808 
10809    /* convert header little endian/big endian */
10810    if (psa->convert_flags) {
10811       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10812 
10813       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10814       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10815       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10816       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10817       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10818       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10819    }
10820 
10821    return header_size + event_size;
10822 }
10823 
10824 
10825 /********************************************************************/
10826 INT recv_event_check(int sock)
10827 /********************************************************************\
10828 
10829   Routine: recv_event_check
10830 
10831   Purpose: Check if in TCP event receive buffer associated with sock
10832            is some data. Called by ss_suspend.
10833 
10834   Input:
10835     INT   sock               TCP receive socket
10836 
10837   Output:
10838     none
10839 
10840   Function value:
10841     INT   count              Number of bytes remaining in TCP buffer
10842 
10843 \********************************************************************/
10844 {
10845    INT idx;
10846 
10847    /* figure out to which connection socket belongs */
10848    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
10849       if (_server_acception[idx].event_sock == sock)
10850          break;
10851 
10852    return _server_acception[idx].ev_write_ptr - _server_acception[idx].ev_read_ptr;
10853 }
10854 
10855 
10856 /********************************************************************/
10857 INT rpc_register_server(INT server_type, const char *name, INT * port, INT(*func) (INT, void **))
10858 /********************************************************************\
10859 
10860   Routine: rpc_register_server
10861 
10862   Purpose: Register the calling process as a MIDAS RPC server. Note
10863            that cm_connnect_experiment must be called prior to any call of
10864            rpc_register_server.
10865 
10866   Input:
10867     INT   server_type       One of the following constants:
10868                             ST_SINGLE: register a single process server
10869                             ST_MTHREAD: for each connection, start
10870                                         a new thread to serve it
10871                             ST_MPROCESS: for each connection, start
10872                                          a new process to server it
10873                             ST_SUBPROCESS: the routine was called from
10874                                            a multi process server
10875                             ST_REMOTE: register a client program server
10876                                        connected to the ODB
10877     char  *name             Name of .EXE file to start in MPROCESS mode
10878     INT   *port             TCP port for listen. NULL if listen as main
10879                             server (MIDAS_TCP_PORT is then used). If *port=0,
10880                             the OS chooses a free port and returns it. If
10881                             *port != 0, this port is used.
10882     INT   *func             Default dispatch function
10883 
10884   Output:
10885     INT   *port             Port under which server is listening.
10886 
10887   Function value:
10888     RPC_SUCCESS             Successful completion
10889     RPC_NET_ERROR           Error in socket call
10890     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10891 
10892 \********************************************************************/
10893 {
10894    struct sockaddr_in bind_addr;
10895    INT status, flag;
10896    unsigned int size;
10897 
10898 #ifdef OS_WINNT
10899    {
10900       WSADATA WSAData;
10901 
10902       /* Start windows sockets */
10903       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10904          return RPC_NET_ERROR;
10905    }
10906 #endif
10907 
10908    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10909 
10910    /* register system functions */
10911    rpc_register_functions(rpc_get_internal_list(0), func);
10912 
10913    if (name != NULL)
10914       rpc_set_server_option(RPC_OSERVER_NAME, (POINTER_T) name);
10915 
10916    /* in subprocess mode, don't start listener */
10917    if (server_type == ST_SUBPROCESS)
10918       return RPC_SUCCESS;
10919 
10920    /* create a socket for listening */
10921    _lsock = socket(AF_INET, SOCK_STREAM, 0);
10922    if (_lsock == -1) {
10923       cm_msg(MERROR, "rpc_register_server", "socket(AF_INET, SOCK_STREAM) failed, errno %d (%s)", errno,
10924              strerror(errno));
10925       return RPC_NET_ERROR;
10926    }
10927 
10928    /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
10929 #if defined(F_SETFD) && defined(FD_CLOEXEC)
10930    status = fcntl(_lsock, F_SETFD, fcntl(_lsock, F_GETFD) | FD_CLOEXEC);
10931    if (status < 0) {
10932       cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno,
10933              strerror(errno));
10934       return RPC_NET_ERROR;
10935    }
10936 #endif
10937 
10938    /* reuse address, needed if previous server stopped (30s timeout!) */
10939    flag = 1;
10940    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
10941    if (status < 0) {
10942       cm_msg(MERROR, "rpc_register_server", "setsockopt(SO_REUSEADDR) failed, errno %d (%s)", errno,
10943              strerror(errno));
10944       return RPC_NET_ERROR;
10945    }
10946 
10947    /* bind local node name and port to socket */
10948    memset(&bind_addr, 0, sizeof(bind_addr));
10949    bind_addr.sin_family = AF_INET;
10950    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
10951 
10952    if (!port)
10953       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
10954    else
10955       bind_addr.sin_port = htons((short) (*port));
10956 
10957    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
10958    if (status < 0) {
10959       cm_msg(MERROR, "rpc_register_server", "bind() failed, errno %d (%s)", errno, strerror(errno));
10960       return RPC_NET_ERROR;
10961    }
10962 
10963    /* listen for connection */
10964 #ifdef OS_MSDOS
10965    status = listen(_lsock, 1);
10966 #else
10967    status = listen(_lsock, SOMAXCONN);
10968 #endif
10969    if (status < 0) {
10970       cm_msg(MERROR, "rpc_register_server", "listen() failed, errno %d (%s)", errno, strerror(errno));
10971       return RPC_NET_ERROR;
10972    }
10973 
10974    /* return port wich OS has choosen */
10975    if (port && *port == 0) {
10976       size = sizeof(bind_addr);
10977 #ifdef OS_WINNT
10978       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
10979 #else
10980       getsockname(_lsock, (struct sockaddr *) &bind_addr, &size);
10981 #endif
10982       *port = ntohs(bind_addr.sin_port);
10983    }
10984 
10985    /* define callbacks for ss_suspend */
10986    if (server_type == ST_REMOTE)
10987       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
10988    else
10989       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
10990 
10991    return RPC_SUCCESS;
10992 }
10993 
10994 typedef struct {
10995    int tid;
10996    int buffer_size;
10997    char *buffer;
10998 } TLS_POINTER;
10999 
11000 TLS_POINTER *tls_buffer = NULL;
11001 int tls_size = 0;
11002 
11003 /********************************************************************/
11004 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
11005 /********************************************************************\
11006 
11007   Routine: rpc_execute
11008 
11009   Purpose: Execute a RPC command received over the network
11010 
11011   Input:
11012     INT  sock               TCP socket to which the result should be
11013                             send back
11014 
11015     char *buffer            Command buffer
11016     INT  convert_flags      Flags for data conversion
11017 
11018   Output:
11019     none
11020 
11021   Function value:
11022     RPC_SUCCESS             Successful completion
11023     RPC_INVALID_ID          Invalid routine_id received
11024     RPC_NET_ERROR           Error in socket call
11025     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11026     RPC_SHUTDOWN            Shutdown requested
11027     SS_ABORT                TCP connection broken
11028     SS_EXIT                 TCP connection closed
11029 
11030 \********************************************************************/
11031 {
11032    INT i, idx, routine_id, status;
11033    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11034    INT tid, flags;
11035    NET_COMMAND *nc_in, *nc_out;
11036    INT param_size, max_size;
11037    void *prpc_param[20];
11038    char str[1024], debug_line[1024], *return_buffer;
11039    int return_buffer_size;
11040    int return_buffer_tls;
11041 #ifdef FIXED_BUFFER
11042    int initial_buffer_size = NET_BUFFER_SIZE;
11043 #else
11044    int initial_buffer_size = 1024;
11045 #endif
11046 
11047    /* return buffer must must use thread local storage multi-thread servers */
11048    if (!tls_size) {
11049       tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
11050       tls_buffer[tls_size].tid = ss_gettid();
11051       tls_buffer[tls_size].buffer_size = initial_buffer_size;
11052       tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
11053       tls_size = 1;
11054    }
11055    for (i = 0; i < tls_size; i++)
11056       if (tls_buffer[i].tid == ss_gettid())
11057          break;
11058    if (i == tls_size) {
11059       /* new thread -> allocate new buffer */
11060       tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size + 1) * sizeof(TLS_POINTER));
11061       tls_buffer[tls_size].tid = ss_gettid();
11062       tls_buffer[tls_size].buffer_size = initial_buffer_size;
11063       tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
11064       tls_size++;
11065    }
11066 
11067    return_buffer_tls = i;
11068    return_buffer_size = tls_buffer[i].buffer_size;
11069    return_buffer = tls_buffer[i].buffer;
11070    assert(return_buffer);
11071 
11072    /* extract pointer array to parameters */
11073    nc_in = (NET_COMMAND *) buffer;
11074 
11075    /* convert header format (byte swapping) */
11076    if (convert_flags) {
11077       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
11078       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
11079    }
11080 
11081    /* no result return in FAST TCP mode */
11082    if (nc_in->header.routine_id & TCP_FAST)
11083       sock = 0;
11084 
11085    /* find entry in rpc_list */
11086    routine_id = nc_in->header.routine_id & ~TCP_FAST;
11087 
11088    assert(rpc_list != NULL);
11089 
11090    for (i = 0;; i++)
11091       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
11092          break;
11093    idx = i;
11094    if (rpc_list[i].id == 0) {
11095       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
11096       return RPC_INVALID_ID;
11097    }
11098 
11099  again:
11100 
11101    in_param_ptr = nc_in->param;
11102 
11103    nc_out = (NET_COMMAND *) return_buffer;
11104    out_param_ptr = nc_out->param;
11105 
11106    sprintf(debug_line, "%s(", rpc_list[idx].name);
11107 
11108    for (i = 0; rpc_list[idx].param[i].tid != 0; i++) {
11109       tid = rpc_list[idx].param[i].tid;
11110       flags = rpc_list[idx].param[i].flags;
11111 
11112       if (flags & RPC_IN) {
11113          param_size = ALIGN8(tid_size[tid]);
11114 
11115          if (tid == TID_STRING || tid == TID_LINK)
11116             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11117 
11118          if (flags & RPC_VARARRAY) {
11119             /* for arrays, the size is stored as a INT in front of the array */
11120             param_size = *((INT *) in_param_ptr);
11121             if (convert_flags)
11122                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
11123             param_size = ALIGN8(param_size);
11124 
11125             in_param_ptr += ALIGN8(sizeof(INT));
11126          }
11127 
11128          if (tid == TID_STRUCT)
11129             param_size = ALIGN8(rpc_list[idx].param[i].n);
11130 
11131          prpc_param[i] = in_param_ptr;
11132 
11133          /* convert data format */
11134          if (convert_flags) {
11135             if (flags & RPC_VARARRAY)
11136                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
11137             else
11138                rpc_convert_data(in_param_ptr, tid, flags, rpc_list[idx].param[i].n * tid_size[tid],
11139                                 convert_flags);
11140          }
11141 
11142          db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[idx].param[i].tid);
11143          if (rpc_list[idx].param[i].tid == TID_STRING) {
11144             /* check for long strings (db_create_record...) */
11145             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11146                strcat(debug_line, "\"");
11147                strcat(debug_line, str);
11148                strcat(debug_line, "\"");
11149             } else
11150                strcat(debug_line, "...");
11151          } else
11152             strcat(debug_line, str);
11153 
11154          in_param_ptr += param_size;
11155       }
11156 
11157       if (flags & RPC_OUT) {
11158          param_size = ALIGN8(tid_size[tid]);
11159 
11160          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11161             /* save maximum array length */
11162             max_size = *((INT *) in_param_ptr);
11163             if (convert_flags)
11164                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
11165             max_size = ALIGN8(max_size);
11166 
11167             *((INT *) out_param_ptr) = max_size;
11168 
11169             /* save space for return array length */
11170             out_param_ptr += ALIGN8(sizeof(INT));
11171 
11172             /* use maximum array length from input */
11173             param_size += max_size;
11174          }
11175 
11176          if (rpc_list[idx].param[i].tid == TID_STRUCT)
11177             param_size = ALIGN8(rpc_list[idx].param[i].n);
11178 
11179          if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
11180 #ifdef FIXED_BUFFER
11181             cm_msg(MERROR, "rpc_execute",
11182                    "return parameters (%d) too large for network buffer (%d)",
11183                    (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size);
11184 
11185             return RPC_EXCEED_BUFFER;
11186 #else
11187             int itls;
11188             int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
11189 
11190             //cm_msg(MINFO, "rpc_execute",
11191             //      "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
11192             //      (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size, new_size);
11193 
11194             itls = return_buffer_tls;
11195 
11196             tls_buffer[itls].buffer_size = new_size;
11197             tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
11198 
11199             if (!tls_buffer[itls].buffer) {
11200                cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
11201                return RPC_EXCEED_BUFFER;
11202             }
11203 
11204             return_buffer_size = tls_buffer[itls].buffer_size;
11205             return_buffer = tls_buffer[itls].buffer;
11206             assert(return_buffer);
11207 
11208             goto again;
11209 #endif
11210          }
11211 
11212          /* if parameter goes both directions, copy input to output */
11213          if (rpc_list[idx].param[i].flags & RPC_IN)
11214             memcpy(out_param_ptr, prpc_param[i], param_size);
11215 
11216          if (_debug_print && !(flags & RPC_IN))
11217             strcat(debug_line, "-");
11218 
11219          prpc_param[i] = out_param_ptr;
11220          out_param_ptr += param_size;
11221       }
11222 
11223       if (rpc_list[idx].param[i + 1].tid)
11224          strcat(debug_line, ", ");
11225    }
11226 
11227    //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
11228 
11229    strcat(debug_line, ")");
11230    rpc_debug_printf(debug_line);
11231 
11232    last_param_ptr = out_param_ptr;
11233 
11234   /*********************************\
11235   *   call dispatch function        *
11236   \*********************************/
11237    if (rpc_list[idx].dispatch)
11238       status = rpc_list[idx].dispatch(routine_id, prpc_param);
11239    else
11240       status = RPC_INVALID_ID;
11241 
11242    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
11243       status = RPC_SUCCESS;
11244 
11245    /* return immediately for closed down client connections */
11246    if (!sock && routine_id == RPC_ID_EXIT)
11247       return SS_EXIT;
11248 
11249    if (!sock && routine_id == RPC_ID_SHUTDOWN)
11250       return RPC_SHUTDOWN;
11251 
11252    /* Return if TCP connection broken */
11253    if (status == SS_ABORT)
11254       return SS_ABORT;
11255 
11256    /* if sock == 0, we are in FTCP mode and may not sent results */
11257    if (!sock)
11258       return RPC_SUCCESS;
11259 
11260    /* compress variable length arrays */
11261    out_param_ptr = nc_out->param;
11262    for (i = 0; rpc_list[idx].param[i].tid != 0; i++)
11263       if (rpc_list[idx].param[i].flags & RPC_OUT) {
11264          tid = rpc_list[idx].param[i].tid;
11265          flags = rpc_list[idx].param[i].flags;
11266          param_size = ALIGN8(tid_size[tid]);
11267 
11268          if (tid == TID_STRING) {
11269             max_size = *((INT *) out_param_ptr);
11270             param_size = strlen((char *) prpc_param[i]) + 1;
11271             param_size = ALIGN8(param_size);
11272 
11273             /* move string ALIGN8(sizeof(INT)) left */
11274             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
11275 
11276             /* move remaining parameters to end of string */
11277             memcpy(out_param_ptr + param_size,
11278                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
11279                    (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
11280          }
11281 
11282          if (flags & RPC_VARARRAY) {
11283             /* store array length at current out_param_ptr */
11284             max_size = *((INT *) out_param_ptr);
11285             param_size = *((INT *) prpc_param[i + 1]);
11286             *((INT *) out_param_ptr) = param_size;      // store new array size
11287             if (convert_flags)
11288                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
11289 
11290             out_param_ptr += ALIGN8(sizeof(INT));       // step over array size
11291 
11292             param_size = ALIGN8(param_size);
11293 
11294             /* move remaining parameters to end of array */
11295             memcpy(out_param_ptr + param_size,
11296                    out_param_ptr + max_size,
11297                    (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
11298          }
11299 
11300          if (tid == TID_STRUCT)
11301             param_size = ALIGN8(rpc_list[idx].param[i].n);
11302 
11303          /* convert data format */
11304          if (convert_flags) {
11305             if (flags & RPC_VARARRAY)
11306                rpc_convert_data(out_param_ptr, tid,
11307                                 rpc_list[idx].param[i].flags | RPC_OUTGOING, param_size, convert_flags);
11308             else
11309                rpc_convert_data(out_param_ptr, tid,
11310                                 rpc_list[idx].param[i].flags | RPC_OUTGOING,
11311                                 rpc_list[idx].param[i].n * tid_size[tid], convert_flags);
11312          }
11313 
11314          out_param_ptr += param_size;
11315       }
11316 
11317    /* send return parameters */
11318    param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
11319    nc_out->header.routine_id = status;
11320    nc_out->header.param_size = param_size;
11321 
11322    //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
11323 
11324    /* convert header format (byte swapping) if necessary */
11325    if (convert_flags) {
11326       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
11327       rpc_convert_single(&nc_out->header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
11328    }
11329 
11330    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
11331 
11332    if (status < 0) {
11333       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11334       return RPC_NET_ERROR;
11335    }
11336 
11337    /* print return buffer */
11338 /*
11339   printf("Return buffer, ID %d:\n", routine_id);
11340   for (i=0; i<param_size ; i++)
11341     {
11342     status = (char) nc_out->param[i];
11343     printf("%02X ", status);
11344     if (i%8 == 7)
11345       printf("\n");
11346     }
11347 */
11348    /* return SS_EXIT if RPC_EXIT is called */
11349    if (routine_id == RPC_ID_EXIT)
11350       return SS_EXIT;
11351 
11352    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11353    if (routine_id == RPC_ID_SHUTDOWN)
11354       return RPC_SHUTDOWN;
11355 
11356    return RPC_SUCCESS;
11357 }
11358 
11359 
11360 /********************************************************************/
11361 INT rpc_execute_ascii(INT sock, char *buffer)
11362 /********************************************************************\
11363 
11364   Routine: rpc_execute_ascii
11365 
11366   Purpose: Execute a RPC command received over the network in ASCII
11367            mode
11368 
11369   Input:
11370     INT  sock               TCP socket to which the result should be
11371                             send back
11372 
11373     char *buffer            Command buffer
11374 
11375   Output:
11376     none
11377 
11378   Function value:
11379     RPC_SUCCESS             Successful completion
11380     RPC_INVALID_ID          Invalid routine_id received
11381     RPC_NET_ERROR           Error in socket call
11382     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11383     RPC_SHUTDOWN            Shutdown requested
11384     SS_ABORT                TCP connection broken
11385     SS_EXIT                 TCP connection closed
11386 
11387 \********************************************************************/
11388 {
11389 #define ASCII_BUFFER_SIZE 64500
11390 #define N_APARAM           1024
11391 
11392    INT i, j, idx, status, index_in;
11393    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11394    INT routine_id, tid, flags, array_tid, n_param;
11395    INT param_size, item_size, num_values;
11396    void *prpc_param[20];
11397    char *arpc_param[N_APARAM], *pc;
11398    char str[1024], debug_line[1024];
11399    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
11400    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
11401    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
11402 
11403    /* parse arguments */
11404    arpc_param[0] = buffer;
11405    for (i = 1; i < N_APARAM; i++) {
11406       arpc_param[i] = strchr(arpc_param[i - 1], '&');
11407       if (arpc_param[i] == NULL)
11408          break;
11409       *arpc_param[i] = 0;
11410       arpc_param[i]++;
11411    }
11412 
11413    /* decode '%' */
11414    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
11415       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
11416          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
11417             str[0] = pc[1];
11418             str[1] = pc[2];
11419             str[2] = 0;
11420             sscanf(str, "%02X", &i);
11421 
11422             *pc++ = i;
11423             while (pc[2]) {
11424                pc[0] = pc[2];
11425                pc++;
11426             }
11427          }
11428       }
11429 
11430    /* find entry in rpc_list */
11431    for (i = 0;; i++)
11432       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
11433          break;
11434    idx = i;
11435    routine_id = rpc_list[i].id;
11436    if (rpc_list[i].id == 0) {
11437       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
11438       return RPC_INVALID_ID;
11439    }
11440 
11441    in_param_ptr = buffer1;
11442    out_param_ptr = buffer2;
11443    index_in = 1;
11444 
11445    sprintf(debug_line, "%s(", rpc_list[idx].name);
11446 
11447    for (i = 0; rpc_list[idx].param[i].tid != 0; i++) {
11448       tid = rpc_list[idx].param[i].tid;
11449       flags = rpc_list[idx].param[i].flags;
11450 
11451       if (flags & RPC_IN) {
11452          if (flags & RPC_VARARRAY) {
11453             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
11454 
11455             prpc_param[i] = in_param_ptr;
11456             for (j = 0; j < n_param; j++) {
11457                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
11458                in_param_ptr += param_size;
11459             }
11460             in_param_ptr = (char *) ALIGN8(((POINTER_T) in_param_ptr));
11461 
11462             strcat(debug_line, "<array>");
11463          } else {
11464             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
11465             param_size = ALIGN8(param_size);
11466 
11467             if (tid == TID_STRING || tid == TID_LINK)
11468                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11469 
11470             /*
11471                if (tid == TID_STRUCT)
11472                param_size = ALIGN8( rpc_list[idx].param[i].n );
11473              */
11474             prpc_param[i] = in_param_ptr;
11475 
11476             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[idx].param[i].tid);
11477             if (rpc_list[idx].param[i].tid == TID_STRING) {
11478                /* check for long strings (db_create_record...) */
11479                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11480                   strcat(debug_line, "\"");
11481                   strcat(debug_line, str);
11482                   strcat(debug_line, "\"");
11483                } else
11484                   strcat(debug_line, "...");
11485             } else
11486                strcat(debug_line, str);
11487 
11488             in_param_ptr += param_size;
11489          }
11490 
11491          if ((POINTER_T) in_param_ptr - (POINTER_T) buffer1 > ASCII_BUFFER_SIZE) {
11492             cm_msg(MERROR, "rpc_ascii_execute",
11493                    "parameters (%d) too large for network buffer (%d)", param_size, ASCII_BUFFER_SIZE);
11494             return RPC_EXCEED_BUFFER;
11495          }
11496 
11497       }
11498 
11499       if (flags & RPC_OUT) {
11500          param_size = ALIGN8(tid_size[tid]);
11501 
11502          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11503             /* reserve maximum array length */
11504             param_size = atoi(arpc_param[index_in]);
11505             param_size = ALIGN8(param_size);
11506          }
11507 
11508 /*
11509       if (rpc_list[idx].param[i].tid == TID_STRUCT)
11510         param_size = ALIGN8( rpc_list[idx].param[i].n );
11511 */
11512          if ((POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size > ASCII_BUFFER_SIZE) {
11513             cm_msg(MERROR, "rpc_execute",
11514                    "return parameters (%d) too large for network buffer (%d)",
11515                    (POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size, ASCII_BUFFER_SIZE);
11516             return RPC_EXCEED_BUFFER;
11517          }
11518 
11519          /* if parameter goes both directions, copy input to output */
11520          if (rpc_list[idx].param[i].flags & RPC_IN)
11521             memcpy(out_param_ptr, prpc_param[i], param_size);
11522 
11523          if (!(flags & RPC_IN))
11524             strcat(debug_line, "-");
11525 
11526          prpc_param[i] = out_param_ptr;
11527          out_param_ptr += param_size;
11528       }
11529 
11530       if (rpc_list[idx].param[i + 1].tid)
11531          strcat(debug_line, ", ");
11532    }
11533 
11534    strcat(debug_line, ")");
11535    rpc_debug_printf(debug_line);
11536 
11537    last_param_ptr = out_param_ptr;
11538 
11539    /*********************************\
11540    *   call dispatch function        *
11541    \*********************************/
11542 
11543    if (rpc_list[idx].dispatch)
11544       status = rpc_list[idx].dispatch(routine_id, prpc_param);
11545    else
11546       status = RPC_INVALID_ID;
11547 
11548    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
11549       status = RPC_SUCCESS;
11550 
11551    /* Return if TCP connection broken */
11552    if (status == SS_ABORT)
11553       return SS_ABORT;
11554 
11555    /* if sock == 0, we are in FTCP mode and may not sent results */
11556    if (!sock)
11557       return RPC_SUCCESS;
11558 
11559    /* send return status */
11560    out_param_ptr = return_buffer;
11561    sprintf(out_param_ptr, "%d", status);
11562    out_param_ptr += strlen(out_param_ptr);
11563 
11564    /* convert return parameters */
11565    for (i = 0; rpc_list[idx].param[i].tid != 0; i++)
11566       if (rpc_list[idx].param[i].flags & RPC_OUT) {
11567          *out_param_ptr++ = '&';
11568 
11569          tid = rpc_list[idx].param[i].tid;
11570          flags = rpc_list[idx].param[i].flags;
11571          param_size = ALIGN8(tid_size[tid]);
11572 
11573          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
11574             strcpy(out_param_ptr, (char *) prpc_param[i]);
11575             param_size = strlen((char *) prpc_param[i]);
11576          }
11577 
11578          else if (flags & RPC_VARARRAY) {
11579             if (rpc_list[idx].id == RPC_BM_RECEIVE_EVENT) {
11580                param_size = *((INT *) prpc_param[i + 1]);
11581                /* write number of bytes to output */
11582                sprintf(out_param_ptr, "%d", param_size);
11583                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
11584                memcpy(out_param_ptr, prpc_param[i], param_size);
11585                out_param_ptr += param_size;
11586                *out_param_ptr = 0;
11587             } else {
11588                if (rpc_list[idx].id == RPC_DB_GET_DATA1) {
11589                   param_size = *((INT *) prpc_param[i + 1]);
11590                   array_tid = *((INT *) prpc_param[i + 2]);
11591                   num_values = *((INT *) prpc_param[i + 3]);
11592                } else if (rpc_list[idx].id == RPC_DB_GET_DATA_INDEX) {
11593                   param_size = *((INT *) prpc_param[i + 1]);
11594                   array_tid = *((INT *) prpc_param[i + 3]);
11595                   num_values = 1;
11596                } else if (rpc_list[idx].id == RPC_HS_READ) {
11597                   param_size = *((INT *) prpc_param[i + 1]);
11598                   if (i == 6) {
11599                      array_tid = TID_DWORD;
11600                      num_values = param_size / sizeof(DWORD);
11601                   } else {
11602                      array_tid = *((INT *) prpc_param[10]);
11603                      num_values = *((INT *) prpc_param[11]);
11604                   }
11605                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
11606 
11607                   param_size = *((INT *) prpc_param[i + 1]);
11608                   array_tid = tid;
11609                   if (tid == TID_STRING)
11610                      num_values = param_size / NAME_LENGTH;
11611                   else
11612                      num_values = param_size / tid_size[tid];
11613                }
11614 
11615                /* derive size of individual item */
11616                if (array_tid == TID_STRING)
11617                   item_size = param_size / num_values;
11618                else
11619                   item_size = tid_size[array_tid];
11620 
11621                /* write number of elements to output */
11622                sprintf(out_param_ptr, "%d", num_values);
11623                out_param_ptr += strlen(out_param_ptr);
11624 
11625                /* write array of values to output */
11626                for (j = 0; j < num_values; j++) {
11627                   *out_param_ptr++ = '&';
11628                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
11629                   out_param_ptr += strlen(out_param_ptr);
11630                }
11631             }
11632          }
11633 
11634 /*
11635       else if (tid == TID_STRUCT)
11636         param_size = ALIGN8( rpc_list[idx].param[i].n );
11637 */
11638          else
11639             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
11640 
11641          out_param_ptr += strlen(out_param_ptr);
11642 
11643          if ((POINTER_T) out_param_ptr - (POINTER_T) return_buffer > ASCII_BUFFER_SIZE) {
11644             cm_msg(MERROR, "rpc_execute",
11645                    "return parameter (%d) too large for network buffer (%d)", param_size, ASCII_BUFFER_SIZE);
11646             return RPC_EXCEED_BUFFER;
11647          }
11648       }
11649 
11650    /* send return parameters */
11651    param_size = (POINTER_T) out_param_ptr - (POINTER_T) return_buffer + 1;
11652 
11653    status = send_tcp(sock, return_buffer, param_size, 0);
11654 
11655    if (status < 0) {
11656       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11657       return RPC_NET_ERROR;
11658    }
11659 
11660    /* print return buffer */
11661    if (strlen(return_buffer) > sizeof(debug_line)) {
11662       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
11663       strcat(debug_line, "...");
11664    } else
11665       sprintf(debug_line, "-> %s", return_buffer);
11666    rpc_debug_printf(debug_line);
11667 
11668    /* return SS_EXIT if RPC_EXIT is called */
11669    if (routine_id == RPC_ID_EXIT)
11670       return SS_EXIT;
11671 
11672    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11673    if (routine_id == RPC_ID_SHUTDOWN)
11674       return RPC_SHUTDOWN;
11675 
11676    return RPC_SUCCESS;
11677 }
11678 
11679 #define MAX_N_ALLOWED_HOSTS 100
11680 static char allowed_host[MAX_N_ALLOWED_HOSTS][256];
11681 static int  n_allowed_hosts = 0;
11682 
11683 /********************************************************************/
11684 INT rpc_clear_allowed_hosts()
11685 /********************************************************************\
11686   Routine: rpc_clear_allowed_hosts
11687 
11688   Purpose: Clear list of allowed hosts and permit connections from anybody
11689 
11690   Input:
11691     none
11692 
11693   Output:
11694     none
11695 
11696   Function value:
11697     RPC_SUCCESS             Successful completion
11698 
11699 \********************************************************************/
11700 {
11701    n_allowed_hosts = 0;
11702    return RPC_SUCCESS;
11703 }
11704 
11705 /********************************************************************/
11706 INT rpc_add_allowed_host(const char* hostname)
11707 /********************************************************************\
11708   Routine: rpc_add_allowed_host
11709 
11710   Purpose: Permit connections from listed hosts only
11711 
11712   Input:
11713     none
11714 
11715   Output:
11716     none
11717 
11718   Function value:
11719     RPC_SUCCESS             Successful completion
11720     RPC_NO_MEMORY           Too many allowed hosts
11721 
11722 \********************************************************************/
11723 {
11724    if (n_allowed_hosts >= MAX_N_ALLOWED_HOSTS)
11725       return RPC_NO_MEMORY;
11726 
11727    strlcpy(allowed_host[n_allowed_hosts++], hostname, sizeof(allowed_host[0]));
11728    return RPC_SUCCESS;
11729 }
11730 
11731 /********************************************************************/
11732 INT rpc_server_accept(int lsock)
11733 /********************************************************************\
11734 
11735   Routine: rpc_server_accept
11736 
11737   Purpose: Accept new incoming connections
11738 
11739   Input:
11740     INT    lscok            Listen socket
11741 
11742   Output:
11743     none
11744 
11745   Function value:
11746     RPC_SUCCESS             Successful completion
11747     RPC_NET_ERROR           Error in socket call
11748     RPC_CONNCLOSED          Connection was closed
11749     RPC_SHUTDOWN            Listener shutdown
11750     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11751 
11752 \********************************************************************/
11753 {
11754    INT idx, i;
11755    unsigned int size;
11756    int status;
11757    char command;
11758    INT sock;
11759    char version[NAME_LENGTH], v1[32];
11760    char experiment[NAME_LENGTH];
11761    INT port1, port2, port3;
11762    char *ptr;
11763    struct sockaddr_in acc_addr;
11764    struct hostent *phe;
11765    char str[100];
11766    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11767    char debug_str[30];
11768    char *argv[10];
11769    char net_buffer[256];
11770    struct linger ling;
11771 
11772    static struct callback_addr callback;
11773 
11774    if (lsock > 0) {
11775       size = sizeof(acc_addr);
11776 #ifdef OS_WINNT
11777       sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11778 #else
11779       sock = accept(lsock, (struct sockaddr *) &acc_addr, &size);
11780 #endif
11781 
11782       if (sock == -1)
11783          return RPC_NET_ERROR;
11784    } else {
11785       /* lsock is stdin -> already connected from inetd */
11786 
11787       size = sizeof(acc_addr);
11788       sock = lsock;
11789 #ifdef OS_WINNT
11790       getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11791 #else
11792       getpeername(sock, (struct sockaddr *) &acc_addr, &size);
11793 #endif
11794    }
11795 
11796    /* check access control list */
11797    if (n_allowed_hosts > 0) {
11798       int allowed = FALSE;
11799       struct hostent *remote_phe;
11800       char hname[256];
11801       struct in_addr remote_addr;
11802 
11803       /* save remote host address */
11804       memcpy(&remote_addr, &(acc_addr.sin_addr), sizeof(remote_addr));
11805 
11806       remote_phe = gethostbyaddr((char *) &remote_addr, 4, PF_INET);
11807 
11808       if (remote_phe == NULL) {
11809          /* use IP number instead */
11810          strlcpy(hname, (char *)inet_ntoa(remote_addr), sizeof(hname));
11811       } else
11812          strlcpy(hname, remote_phe->h_name, sizeof(hname));
11813 
11814       /* always permit localhost */
11815       if (strcmp(hname, "localhost.localdomain") == 0)
11816          allowed = TRUE;
11817       if (strcmp(hname, "localhost") == 0)
11818          allowed = TRUE;
11819 
11820       if (!allowed) {
11821          for (i=0 ; i<n_allowed_hosts ; i++)
11822             if (strcmp(hname, allowed_host[i]) == 0) {
11823                allowed = TRUE;
11824                break;
11825             }
11826       }
11827 
11828       if (!allowed) {
11829          cm_msg(MERROR, "rpc_server_accept", "rejecting connection from unallowed host \'%s\'", hname);
11830          closesocket(sock);
11831          return RPC_NET_ERROR;
11832       }
11833    }
11834 
11835    /* receive string with timeout */
11836    i = recv_string(sock, net_buffer, 256, 10000);
11837    rpc_debug_printf("Received command: %s", net_buffer);
11838 
11839    if (i > 0) {
11840       command = (char) toupper(net_buffer[0]);
11841 
11842       switch (command) {
11843       case 'S':
11844 
11845          /*----------- shutdown listener ----------------------*/
11846          closesocket(sock);
11847          return RPC_SHUTDOWN;
11848 
11849       case 'I':
11850 
11851          /*----------- return available experiments -----------*/
11852          cm_scan_experiments();
11853          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11854             rpc_debug_printf("Return experiment: %s", exptab[i].name);
11855             sprintf(str, "%s", exptab[i].name);
11856             send(sock, str, strlen(str) + 1, 0);
11857          }
11858          send(sock, "", 1, 0);
11859          closesocket(sock);
11860          break;
11861 
11862       case 'C':
11863 
11864          /*----------- connect to experiment -----------*/
11865 
11866          /* get callback information */
11867          callback.experiment[0] = 0;
11868          port1 = port2 = version[0] = 0;
11869 
11870          //printf("net buffer \'%s\'\n", net_buffer);
11871 
11872          /* parse string in format "C port1 port2 port3 version expt" */
11873          /* example: C 51046 45838 56832 2.0.0 alpha */
11874 
11875          port1 = strtoul(net_buffer + 2, &ptr, 0);
11876          port2 = strtoul(ptr, &ptr, 0);
11877          port3 = strtoul(ptr, &ptr, 0);
11878 
11879          while (*ptr == ' ')
11880             ptr++;
11881 
11882          i = 0;
11883          for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
11884             version[i++] = *ptr++;
11885 
11886          // ensure that we do not overwrite buffer "version"
11887          assert(i < (int) sizeof(version));
11888          version[i] = 0;
11889 
11890          // skip wjatever is left from the "version" string
11891          for (; *ptr != 0 && *ptr != ' ';)
11892             ptr++;
11893 
11894          while (*ptr == ' ')
11895             ptr++;
11896 
11897          i = 0;
11898          for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
11899             experiment[i++] = *ptr++;
11900 
11901          // ensure that we do not overwrite buffer "experiment"
11902          assert(i < (int) sizeof(experiment));
11903          experiment[i] = 0;
11904 
11905          strlcpy(callback.experiment, experiment, NAME_LENGTH);
11906 
11907          /* print warning if version patch level doesn't agree */
11908          strlcpy(v1, version, sizeof(v1));
11909          if (strchr(v1, '.'))
11910             if (strchr(strchr(v1, '.') + 1, '.'))
11911                *strchr(strchr(v1, '.') + 1, '.') = 0;
11912 
11913          strlcpy(str, cm_get_version(), sizeof(str));
11914          if (strchr(str, '.'))
11915             if (strchr(strchr(str, '.') + 1, '.'))
11916                *strchr(strchr(str, '.') + 1, '.') = 0;
11917 
11918          if (strcmp(v1, str) != 0) {
11919             sprintf(str, "client MIDAS version %s differs from local version %s", version, cm_get_version());
11920             cm_msg(MERROR, "rpc_server_accept", str);
11921 
11922             sprintf(str, "received string: %s", net_buffer + 2);
11923             cm_msg(MERROR, "rpc_server_accept", str);
11924          }
11925 
11926          callback.host_port1 = (short) port1;
11927          callback.host_port2 = (short) port2;
11928          callback.host_port3 = (short) port3;
11929          callback.debug = _debug_mode;
11930 
11931          /* get the name of the remote host */
11932 #ifdef OS_VXWORKS
11933          {
11934             INT status;
11935             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11936             if (status != 0) {
11937                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11938                break;
11939             }
11940          }
11941 #else
11942          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11943          if (phe == NULL) {
11944             /* use IP number instead */
11945             strlcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr), HOST_NAME_LENGTH);
11946          } else
11947             strlcpy(callback.host_name, phe->h_name, HOST_NAME_LENGTH);
11948 #endif
11949 
11950          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11951             /* update experiment definition */
11952             cm_scan_experiments();
11953 
11954             /* lookup experiment */
11955             if (equal_ustring(callback.experiment, "Default"))
11956                idx = 0;
11957             else
11958                for (idx = 0; idx < MAX_EXPERIMENT && exptab[idx].name[0]; idx++)
11959                   if (equal_ustring(callback.experiment, exptab[idx].name))
11960                      break;
11961 
11962             if (idx == MAX_EXPERIMENT || exptab[idx].name[0] == 0) {
11963                sprintf(str, "experiment \'%s\' not defined in exptab\r", callback.experiment);
11964                cm_msg(MERROR, "rpc_server_accept", str);
11965 
11966                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11967                closesocket(sock);
11968                break;
11969             }
11970 
11971             strlcpy(callback.directory, exptab[idx].directory, MAX_STRING_LENGTH);
11972             strlcpy(callback.user, exptab[idx].user, NAME_LENGTH);
11973 
11974             /* create a new process */
11975             sprintf(host_port1_str, "%d", callback.host_port1);
11976             sprintf(host_port2_str, "%d", callback.host_port2);
11977             sprintf(host_port3_str, "%d", callback.host_port3);
11978             sprintf(debug_str, "%d", callback.debug);
11979 
11980             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11981             argv[1] = callback.host_name;
11982             argv[2] = host_port1_str;
11983             argv[3] = host_port2_str;
11984             argv[4] = host_port3_str;
11985             argv[5] = debug_str;
11986             argv[6] = callback.experiment;
11987             argv[7] = callback.directory;
11988             argv[8] = callback.user;
11989             argv[9] = NULL;
11990 
11991             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
11992                              argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
11993                              argv[9]);
11994 
11995             status = ss_spawnv(P_NOWAIT, (char *) rpc_get_server_option(RPC_OSERVER_NAME), argv);
11996 
11997             if (status != SS_SUCCESS) {
11998                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
11999 
12000                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
12001                send(sock, str, strlen(str) + 1, 0);
12002                closesocket(sock);
12003                break;
12004             }
12005 
12006             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
12007             send(sock, str, strlen(str) + 1, 0);
12008             closesocket(sock);
12009          } else {
12010             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
12011             send(sock, str, strlen(str) + 1, 0);
12012             closesocket(sock);
12013          }
12014 
12015          /* look for next free entry */
12016          for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
12017             if (_server_acception[idx].recv_sock == 0)
12018                break;
12019          if (idx == MAX_RPC_CONNECTION)
12020             return RPC_NET_ERROR;
12021          callback.index = idx;
12022 
12023         /*----- multi thread server ------------------------*/
12024          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
12025             ss_thread_create(rpc_server_thread, (void *) (&callback));
12026 
12027         /*----- single thread server -----------------------*/
12028          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
12029              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
12030             rpc_server_callback(&callback);
12031 
12032          break;
12033 
12034       default:
12035          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
12036          closesocket(sock);
12037          break;
12038 
12039       }
12040    } else {                     /* if i>0 */
12041 
12042       /* lingering needed for PCTCP */
12043       ling.l_onoff = 1;
12044       ling.l_linger = 0;
12045       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12046       closesocket(sock);
12047    }
12048 
12049    return RPC_SUCCESS;
12050 }
12051 
12052 /********************************************************************/
12053 INT rpc_client_accept(int lsock)
12054 /********************************************************************\
12055 
12056   Routine: rpc_client_accept
12057 
12058   Purpose: Accept new incoming connections as a client
12059 
12060   Input:
12061     INT    lsock            Listen socket
12062 
12063   Output:
12064     none
12065 
12066   Function value:
12067     RPC_SUCCESS             Successful completion
12068     RPC_NET_ERROR           Error in socket call
12069     RPC_CONNCLOSED          Connection was closed
12070     RPC_SHUTDOWN            Listener shutdown
12071     RPC_EXCEED_BUFFER       Not enough memory for network buffer
12072 
12073 \********************************************************************/
12074 {
12075    INT idx, i, version, status;
12076    unsigned int size;
12077    int sock;
12078    struct sockaddr_in acc_addr;
12079    INT client_hw_type = 0, hw_type;
12080    char str[100], client_program[NAME_LENGTH];
12081    char host_name[HOST_NAME_LENGTH];
12082    INT convert_flags;
12083    char net_buffer[256], *p;
12084 
12085    size = sizeof(acc_addr);
12086 #ifdef OS_WINNT
12087    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
12088 #else
12089    sock = accept(lsock, (struct sockaddr *) &acc_addr, &size);
12090 #endif
12091 
12092    if (sock == -1)
12093       return RPC_NET_ERROR;
12094 
12095    /* get the name of the calling host */
12096 /* outcommented for speed reasons SR 7.10.98
12097 #ifdef OS_VXWORKS
12098   {
12099   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
12100   if (status != 0)
12101     strcpy(host_name, "unknown");
12102   }
12103 #else
12104   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
12105   if (phe == NULL)
12106     strcpy(host_name, "unknown");
12107   strcpy(host_name, phe->h_name);
12108 #endif
12109 */
12110    strcpy(host_name, "(unknown)");
12111 
12112    strcpy(client_program, "(unknown)");
12113 
12114    /* look for next free entry */
12115    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
12116       if (_server_acception[idx].recv_sock == 0)
12117          break;
12118    if (idx == MAX_RPC_CONNECTION) {
12119       closesocket(sock);
12120       return RPC_NET_ERROR;
12121    }
12122 
12123    /* receive string with timeout */
12124    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
12125    if (i <= 0) {
12126       closesocket(sock);
12127       return RPC_NET_ERROR;
12128    }
12129 
12130    /* get remote computer info */
12131    p = strtok(net_buffer, " ");
12132    if (p != NULL) {
12133       client_hw_type = atoi(p);
12134       p = strtok(NULL, " ");
12135    }
12136    if (p != NULL) {
12137       version = atoi(p);
12138       p = strtok(NULL, " ");
12139    }
12140    if (p != NULL) {
12141       strlcpy(client_program, p, sizeof(client_program));
12142       p = strtok(NULL, " ");
12143    }
12144    if (p != NULL) {
12145       strlcpy(host_name, p, sizeof(host_name));
12146       p = strtok(NULL, " ");
12147    }
12148 
12149    if (0)
12150       printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n",
12151              client_hw_type, version, client_program, host_name);
12152 
12153    /* save information in _server_acception structure */
12154    _server_acception[idx].recv_sock = sock;
12155    _server_acception[idx].send_sock = 0;
12156    _server_acception[idx].event_sock = 0;
12157    _server_acception[idx].remote_hw_type = client_hw_type;
12158    strcpy(_server_acception[idx].host_name, host_name);
12159    strcpy(_server_acception[idx].prog_name, client_program);
12160    _server_acception[idx].tid = ss_gettid();
12161    _server_acception[idx].last_activity = ss_millitime();
12162    _server_acception[idx].watchdog_timeout = 0;
12163 
12164    /* send my own computer id */
12165    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
12166    sprintf(str, "%d %s", hw_type, cm_get_version());
12167    status = send(sock, str, strlen(str) + 1, 0);
12168    if (status != (INT) strlen(str) + 1)
12169       return RPC_NET_ERROR;
12170 
12171    rpc_set_server_acception(idx + 1);
12172    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
12173    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
12174 
12175    /* set callback function for ss_suspend */
12176    ss_suspend_set_dispatch(CH_SERVER, _server_acception, (int (*)(void)) rpc_server_receive);
12177 
12178    return RPC_SUCCESS;
12179 }
12180 
12181 /********************************************************************/
12182 INT rpc_server_callback(struct callback_addr * pcallback)
12183 /********************************************************************\
12184 
12185   Routine: rpc_server_callback
12186 
12187   Purpose: Callback a remote client. Setup _server_acception entry
12188            with optional conversion flags and establish two-way
12189            TCP connection.
12190 
12191   Input:
12192     callback_addr pcallback Pointer to a callback structure
12193 
12194   Output:
12195     none
12196 
12197   Function value:
12198     RPC_SUCCESS             Successful completion
12199 
12200 \********************************************************************/
12201 {
12202    INT idx, status;
12203    int recv_sock, send_sock, event_sock;
12204    struct sockaddr_in bind_addr;
12205    struct hostent *phe;
12206    char str[100], client_program[NAME_LENGTH];
12207    char host_name[HOST_NAME_LENGTH];
12208    INT client_hw_type, hw_type;
12209    INT convert_flags;
12210    char net_buffer[256];
12211    struct callback_addr callback;
12212    char *p;
12213    int flag;
12214 
12215    /* copy callback information */
12216    memcpy(&callback, pcallback, sizeof(callback));
12217    idx = callback.index;
12218 
12219    /* create new sockets for TCP */
12220    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
12221    send_sock = socket(AF_INET, SOCK_STREAM, 0);
12222    event_sock = socket(AF_INET, SOCK_STREAM, 0);
12223    if (event_sock == -1)
12224       return RPC_NET_ERROR;
12225 
12226    /* callback to remote node */
12227    memset(&bind_addr, 0, sizeof(bind_addr));
12228    bind_addr.sin_family = AF_INET;
12229    bind_addr.sin_port = htons(callback.host_port1);
12230 
12231 #ifdef OS_VXWORKS
12232    {
12233       INT host_addr;
12234 
12235       host_addr = hostGetByName(callback.host_name);
12236       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
12237    }
12238 #else
12239    phe = gethostbyname(callback.host_name);
12240    if (phe == NULL) {
12241       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
12242       return RPC_NET_ERROR;
12243    }
12244    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
12245 #endif
12246 
12247    /* connect receive socket */
12248 #ifdef OS_UNIX
12249    do {
12250       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
12251 
12252       /* don't return if an alarm signal was cought */
12253    } while (status == -1 && errno == EINTR);
12254 #else
12255    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12256 #endif
12257 
12258    if (status != 0) {
12259       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
12260       goto error;
12261    }
12262 
12263    bind_addr.sin_port = htons(callback.host_port2);
12264 
12265    /* connect send socket */
12266 #ifdef OS_UNIX
12267    do {
12268       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12269 
12270       /* don't return if an alarm signal was cought */
12271    } while (status == -1 && errno == EINTR);
12272 #else
12273    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12274 #endif
12275 
12276    if (status != 0) {
12277       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
12278       goto error;
12279    }
12280 
12281    bind_addr.sin_port = htons(callback.host_port3);
12282 
12283    /* connect event socket */
12284 #ifdef OS_UNIX
12285    do {
12286       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12287 
12288       /* don't return if an alarm signal was cought */
12289    } while (status == -1 && errno == EINTR);
12290 #else
12291    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12292 #endif
12293 
12294    if (status != 0) {
12295       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
12296       goto error;
12297    }
12298 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
12299    /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12300    flag = 2 * 1024 * 1024;
12301    status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
12302    if (status != 0)
12303       cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
12304              strerror(errno));
12305 #endif
12306 
12307    if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
12308       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
12309       goto error;
12310    }
12311    //printf("rpc_server_callback: \'%s\'\n", net_buffer);
12312 
12313    /* get remote computer info */
12314    client_hw_type = strtoul(net_buffer, &p, 0);
12315 
12316    while (*p == ' ')
12317       p++;
12318 
12319    strlcpy(client_program, p, sizeof(client_program));
12320 
12321    //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
12322 
12323    /* get the name of the remote host */
12324 #ifdef OS_VXWORKS
12325    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
12326    if (status != 0)
12327       strcpy(host_name, "unknown");
12328 #else
12329    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
12330    if (phe == NULL)
12331       strcpy(host_name, "unknown");
12332    else
12333       strcpy(host_name, phe->h_name);
12334 #endif
12335 
12336    /* save information in _server_acception structure */
12337    _server_acception[idx].recv_sock = recv_sock;
12338    _server_acception[idx].send_sock = send_sock;
12339    _server_acception[idx].event_sock = event_sock;
12340    _server_acception[idx].remote_hw_type = client_hw_type;
12341    strcpy(_server_acception[idx].host_name, host_name);
12342    strcpy(_server_acception[idx].prog_name, client_program);
12343    _server_acception[idx].tid = ss_gettid();
12344    _server_acception[idx].last_activity = ss_millitime();
12345    _server_acception[idx].watchdog_timeout = 0;
12346 
12347    /* send my own computer id */
12348    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
12349    sprintf(str, "%d", hw_type);
12350    send(recv_sock, str, strlen(str) + 1, 0);
12351 
12352    rpc_set_server_acception(idx + 1);
12353    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
12354    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
12355 
12356    /* set callback function for ss_suspend */
12357    ss_suspend_set_dispatch(CH_SERVER, _server_acception, (int (*)(void)) rpc_server_receive);
12358 
12359    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12360       rpc_debug_printf("Connection to %s:%s established\n",
12361                        _server_acception[idx].host_name, _server_acception[idx].prog_name);
12362 
12363    return RPC_SUCCESS;
12364 
12365  error:
12366 
12367    closesocket(recv_sock);
12368    closesocket(send_sock);
12369    closesocket(event_sock);
12370 
12371    return RPC_NET_ERROR;
12372 }
12373 
12374 
12375 /********************************************************************/
12376 INT rpc_server_thread(void *pointer)
12377 /********************************************************************\
12378 
12379   Routine: rpc_server_thread
12380 
12381   Purpose: New thread for a multi-threaded server. Callback to the
12382            client and process RPC requests.
12383 
12384   Input:
12385     vcoid  pointer          pointer to callback_addr structure.
12386 
12387   Output:
12388     none
12389 
12390   Function value:
12391     RPC_SUCCESS             Successful completion
12392 
12393 \********************************************************************/
12394 {
12395    struct callback_addr callback;
12396    int status, semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg;
12397    static DWORD last_checked = 0;
12398 
12399    memcpy(&callback, pointer, sizeof(callback));
12400 
12401    status = rpc_server_callback(&callback);
12402 
12403    if (status != RPC_SUCCESS)
12404       return status;
12405 
12406    /* create alarm and elog semaphores */
12407    ss_semaphore_create("ALARM", &semaphore_alarm);
12408    ss_semaphore_create("ELOG", &semaphore_elog);
12409    ss_semaphore_create("HISTORY", &semaphore_history);
12410    ss_semaphore_create("MSG", &semaphore_msg);
12411    cm_set_experiment_semaphore(semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg);
12412 
12413    do {
12414       status = ss_suspend(5000, 0);
12415 
12416       if (rpc_check_channels() == RPC_NET_ERROR)
12417          break;
12418 
12419       /* check alarms every 10 seconds */
12420       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
12421          al_check();
12422          last_checked = ss_time();
12423       }
12424 
12425    } while (status != SS_ABORT && status != SS_EXIT);
12426 
12427    /* delete entry in suspend table for this thread */
12428    ss_suspend_exit();
12429 
12430    return RPC_SUCCESS;
12431 }
12432 
12433 
12434 /********************************************************************/
12435 INT rpc_server_receive(INT idx, int sock, BOOL check)
12436 /********************************************************************\
12437 
12438   Routine: rpc_server_receive
12439 
12440   Purpose: Receive rpc commands and execute them. Close the connection
12441            if client has broken TCP pipe.
12442 
12443   Input:
12444     INT    idx              Index to _server_acception structure in-
12445                             dicating which connection got data.
12446     int    sock             Socket which got data
12447     BOOL   check            If TRUE, only check if connection is
12448                             broken. This may be called via
12449                             bm_receive_event/ss_suspend(..,MSG_BM)
12450 
12451   Output:
12452     none
12453 
12454   Function value:
12455     RPC_SUCCESS             Successful completion
12456     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
12457     SS_EXIT                 Server connection was closed
12458     SS_ABORT                Server connection was broken
12459 
12460 \********************************************************************/
12461 {
12462    INT status, n_received;
12463    INT remaining, *pbh, start_time;
12464    char test_buffer[256], str[80];
12465    EVENT_HEADER *pevent;
12466 
12467    /* init network buffer */
12468    if (_net_recv_buffer_size == 0) {
12469       int size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + 1024;
12470       _net_recv_buffer = (char *) M_MALLOC(size);
12471       if (_net_recv_buffer == NULL) {
12472          cm_msg(MERROR, "rpc_server_receive", "Cannot allocate %d bytes for network buffer", size);
12473          return RPC_EXCEED_BUFFER;
12474       }
12475       _net_recv_buffer_size = size;
12476       _net_recv_buffer_size_odb = 0;
12477       //printf("allocated _net_recv_buffer size %d\n", _net_recv_buffer_size);
12478    }
12479 
12480    /* init network buffer */
12481    if (_net_recv_buffer_size_odb == 0) {
12482       HNDLE hDB;
12483       int size;
12484       int max_event_size = MAX_EVENT_SIZE;
12485 
12486       /* get max event size from ODB */
12487       status = cm_get_experiment_database(&hDB, NULL);
12488       assert(status == CM_SUCCESS);
12489 
12490       if (hDB) {
12491          size = sizeof(INT);
12492          status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &max_event_size, &size, TID_DWORD, TRUE);
12493 
12494          size = max_event_size + sizeof(EVENT_HEADER) + 1024;
12495 
12496          _net_recv_buffer_size_odb = size;
12497 
12498          if (size > _net_recv_buffer_size) {
12499             _net_recv_buffer = (char *) realloc(_net_recv_buffer, size);
12500             if (_net_recv_buffer == NULL) {
12501                cm_msg(MERROR, "rpc_server_receive", "Cannot allocate %d bytes for network buffer", size);
12502                return RPC_EXCEED_BUFFER;
12503             }
12504             _net_recv_buffer_size = size;
12505             //printf("allocated _net_recv_buffer size %d\n", _net_recv_buffer_size);
12506          }
12507       }
12508    }
12509 
12510    /* only check if TCP connection is broken */
12511    if (check) {
12512       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
12513 
12514       if (n_received == -1)
12515          cm_msg(MERROR, "rpc_server_receive",
12516                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
12517                 n_received, errno, strerror(errno));
12518 
12519       if (n_received <= 0)
12520          return SS_ABORT;
12521 
12522       return SS_SUCCESS;
12523    }
12524 
12525    remaining = 0;
12526 
12527    /* receive command */
12528    if (sock == _server_acception[idx].recv_sock) {
12529       do {
12530          if (_server_acception[idx].remote_hw_type == DR_ASCII)
12531             n_received =
12532                 recv_string(_server_acception[idx].recv_sock, _net_recv_buffer, _net_recv_buffer_size, 10000);
12533          else
12534             n_received = recv_tcp_server(idx, _net_recv_buffer, _net_recv_buffer_size, 0, &remaining);
12535 
12536          if (n_received <= 0) {
12537             status = SS_ABORT;
12538             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort", n_received);
12539             goto error;
12540          }
12541 
12542          rpc_set_server_acception(idx + 1);
12543 
12544          if (_server_acception[idx].remote_hw_type == DR_ASCII)
12545             status = rpc_execute_ascii(_server_acception[idx].recv_sock, _net_recv_buffer);
12546          else
12547             status = rpc_execute(_server_acception[idx].recv_sock,
12548                                  _net_recv_buffer, _server_acception[idx].convert_flags);
12549 
12550          if (status == SS_ABORT) {
12551             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort", status);
12552             goto error;
12553          }
12554 
12555          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
12556             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12557                rpc_debug_printf("Connection to %s:%s closed\n",
12558                                 _server_acception[idx].host_name, _server_acception[idx].prog_name);
12559             goto exit;
12560          }
12561 
12562       } while (remaining);
12563    } else {
12564       /* receive event */
12565       if (sock == _server_acception[idx].event_sock) {
12566          start_time = ss_millitime();
12567 
12568          do {
12569             n_received = recv_event_server(idx, _net_recv_buffer, _net_recv_buffer_size, 0, &remaining);
12570 
12571             if (n_received <= 0) {
12572                status = SS_ABORT;
12573                cm_msg(MERROR, "rpc_server_receive", "recv_event_server() returned %d, abort", n_received);
12574                goto error;
12575             }
12576 
12577             /* send event to buffer */
12578             pbh = (INT *) _net_recv_buffer;
12579             pevent = (EVENT_HEADER *) (pbh + 1);
12580 
12581             status = bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
12582             if (status != BM_SUCCESS)
12583                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d", status);
12584 
12585             /* repeat for maximum 0.5 sec */
12586          } while (ss_millitime() - start_time < 500 && remaining);
12587       }
12588    }
12589 
12590    return RPC_SUCCESS;
12591 
12592  error:
12593 
12594    strlcpy(str, _server_acception[idx].host_name, sizeof(str));
12595    if (strchr(str, '.'))
12596       *strchr(str, '.') = 0;
12597    cm_msg(MTALK, "rpc_server_receive", "Program \'%s\' on host \'%s\' aborted",
12598           _server_acception[idx].prog_name, str);
12599 
12600  exit:
12601 
12602    /* disconnect from experiment as MIDAS server */
12603    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
12604       HNDLE hDB, hKey;
12605 
12606       cm_get_experiment_database(&hDB, &hKey);
12607 
12608       /* only disconnect from experiment if previously connected.
12609          Necessary for pure RPC servers (RPC_SRVR) */
12610       if (hDB) {
12611 #ifdef LOCAL_ROUTINES
12612          ss_alarm(0, cm_watchdog);
12613 #endif
12614 
12615          cm_delete_client_info(hDB, 0);
12616 
12617          bm_close_all_buffers();
12618          db_close_all_databases();
12619 
12620          rpc_deregister_functions();
12621 
12622          cm_set_experiment_database(0, 0);
12623 
12624          _msg_buffer = 0;
12625       }
12626    }
12627 
12628    /* close server connection */
12629    if (_server_acception[idx].recv_sock)
12630       closesocket(_server_acception[idx].recv_sock);
12631    if (_server_acception[idx].send_sock)
12632       closesocket(_server_acception[idx].send_sock);
12633    if (_server_acception[idx].event_sock)
12634       closesocket(_server_acception[idx].event_sock);
12635 
12636    /* free TCP cache */
12637    M_FREE(_server_acception[idx].net_buffer);
12638    _server_acception[idx].net_buffer = NULL;
12639 
12640    /* mark this entry as invalid */
12641    memset(&_server_acception[idx], 0, sizeof(RPC_SERVER_ACCEPTION));
12642 
12643    /* signal caller a shutdonw */
12644    if (status == RPC_SHUTDOWN)
12645       return status;
12646 
12647    /* don't abort if other than main connection is broken */
12648    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
12649       return SS_SUCCESS;
12650 
12651    return status;
12652 }
12653 
12654 
12655 /********************************************************************/
12656 INT rpc_server_shutdown(void)
12657 /********************************************************************\
12658 
12659   Routine: rpc_server_shutdown
12660 
12661   Purpose: Shutdown RPC server, abort all connections
12662 
12663   Input:
12664     none
12665 
12666   Output:
12667     none
12668 
12669   Function value:
12670     RPC_SUCCESS             Successful completion
12671 
12672 \********************************************************************/
12673 {
12674    INT i;
12675    struct linger ling;
12676 
12677    /* close all open connections */
12678    for (i = 0; i < MAX_RPC_CONNECTION; i++)
12679       if (_server_acception[i].recv_sock != 0) {
12680          /* lingering needed for PCTCP */
12681          ling.l_onoff = 1;
12682          ling.l_linger = 0;
12683          setsockopt(_server_acception[i].recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12684          closesocket(_server_acception[i].recv_sock);
12685 
12686          if (_server_acception[i].send_sock) {
12687             setsockopt(_server_acception[i].send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12688             closesocket(_server_acception[i].send_sock);
12689          }
12690 
12691          if (_server_acception[i].event_sock) {
12692             setsockopt(_server_acception[i].event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12693             closesocket(_server_acception[i].event_sock);
12694          }
12695 
12696          _server_acception[i].recv_sock = 0;
12697          _server_acception[i].send_sock = 0;
12698          _server_acception[i].event_sock = 0;
12699       }
12700 
12701    if (_lsock) {
12702       closesocket(_lsock);
12703       _lsock = 0;
12704       _server_registered = FALSE;
12705    }
12706 
12707    /* free suspend structures */
12708    ss_suspend_exit();
12709 
12710    return RPC_SUCCESS;
12711 }
12712 
12713 
12714 /********************************************************************/
12715 INT rpc_check_channels(void)
12716 /********************************************************************\
12717 
12718   Routine: rpc_check_channels
12719 
12720   Purpose: Check open rpc channels by sending watchdog messages
12721 
12722   Input:
12723     none
12724 
12725   Output:
12726     none
12727 
12728   Function value:
12729     RPC_SUCCESS             Channel is still alive
12730     RPC_NET_ERROR           Connection is broken
12731 
12732 \********************************************************************/
12733 {
12734    INT status, idx, i, convert_flags;
12735    NET_COMMAND nc;
12736    fd_set readfds;
12737    struct timeval timeout;
12738 
12739    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++) {
12740       if (_server_acception[idx].recv_sock &&
12741           _server_acception[idx].tid == ss_gettid() &&
12742           _server_acception[idx].watchdog_timeout &&
12743           (ss_millitime() - _server_acception[idx].last_activity >
12744            (DWORD) _server_acception[idx].watchdog_timeout)) {
12745 /* printf("Send watchdog message to %s on %s\n",
12746                 _server_acception[idx].prog_name,
12747                 _server_acception[idx].host_name); */
12748 
12749          /* send a watchdog message */
12750          nc.header.routine_id = MSG_WATCHDOG;
12751          nc.header.param_size = 0;
12752 
12753          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
12754          if (convert_flags) {
12755             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
12756             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
12757          }
12758 
12759          /* send the header to the client */
12760          i = send_tcp(_server_acception[idx].send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
12761 
12762          if (i < 0)
12763             goto exit;
12764 
12765          /* make some timeout checking */
12766          FD_ZERO(&readfds);
12767          FD_SET(_server_acception[idx].send_sock, &readfds);
12768          FD_SET(_server_acception[idx].recv_sock, &readfds);
12769 
12770          timeout.tv_sec = _server_acception[idx].watchdog_timeout / 1000;
12771          timeout.tv_usec = (_server_acception[idx].watchdog_timeout % 1000) * 1000;
12772 
12773          do {
12774             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12775 
12776             /* if an alarm signal was cought, restart select with reduced timeout */
12777             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12778                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12779 
12780          } while (status == -1);        /* dont return if an alarm signal was cought */
12781 
12782          if (!FD_ISSET(_server_acception[idx].send_sock, &readfds) &&
12783              !FD_ISSET(_server_acception[idx].recv_sock, &readfds))
12784             goto exit;
12785 
12786          /* receive result on send socket */
12787          if (FD_ISSET(_server_acception[idx].send_sock, &readfds)) {
12788             i = recv_tcp(_server_acception[idx].send_sock, (char *) &nc, sizeof(nc), 0);
12789             if (i <= 0)
12790                goto exit;
12791          }
12792       }
12793    }
12794 
12795    return RPC_SUCCESS;
12796 
12797  exit:
12798 
12799    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
12800           _server_acception[idx].host_name,
12801           _server_acception[idx].prog_name, _server_acception[idx].watchdog_timeout / 1000);
12802 
12803    /* disconnect from experiment */
12804    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12805       cm_disconnect_experiment();
12806 
12807    /* close server connection */
12808    if (_server_acception[idx].recv_sock)
12809       closesocket(_server_acception[idx].recv_sock);
12810    if (_server_acception[idx].send_sock)
12811       closesocket(_server_acception[idx].send_sock);
12812    if (_server_acception[idx].event_sock)
12813       closesocket(_server_acception[idx].event_sock);
12814 
12815    /* free TCP cache */
12816    M_FREE(_server_acception[idx].net_buffer);
12817    _server_acception[idx].net_buffer = NULL;
12818 
12819    /* mark this entry as invalid */
12820    memset(&_server_acception[idx], 0, sizeof(RPC_SERVER_ACCEPTION));
12821 
12822    return RPC_NET_ERROR;
12823 }
12824 
12825 /**dox***************************************************************/
12826 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12827 
12828 /**dox***************************************************************/
12829                                                                                                                                /** @} *//* end of rpcfunctionc */
12830 
12831 /**dox***************************************************************/
12832 /** @addtogroup bkfunctionc
12833  *
12834  *  @{  */
12835 
12836 /********************************************************************\
12837 *                                                                    *
12838 *                 Bank functions                                     *
12839 *                                                                    *
12840 \********************************************************************/
12841 
12842 /********************************************************************/
12843 /**
12844 Initializes an event for Midas banks structure.
12845 Before banks can be created in an event, bk_init() has to be called first.
12846 @param event pointer to the area of event
12847 */
12848 void bk_init(void *event)
12849 {
12850    ((BANK_HEADER *) event)->data_size = 0;
12851    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
12852 }
12853 
12854 /**dox***************************************************************/
12855 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12856 
12857 /********************************************************************/
12858 BOOL bk_is32(void *event)
12859 /********************************************************************\
12860 
12861   Routine: bk_is32
12862 
12863   Purpose: Return true if banks inside event are 32-bit banks
12864 
12865   Input:
12866     void   *event           pointer to the event
12867 
12868   Output:
12869     none
12870 
12871   Function value:
12872     none
12873 
12874 \********************************************************************/
12875 {
12876    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12877 }
12878 
12879 /**dox***************************************************************/
12880 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12881 
12882 /********************************************************************/
12883 /**
12884 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12885 Before banks can be created in an event, bk_init32() has to be called first.
12886 @param event pointer to the area of event
12887 @return void
12888 */
12889 void bk_init32(void *event)
12890 {
12891    ((BANK_HEADER *) event)->data_size = 0;
12892    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12893 }
12894 
12895 /********************************************************************/
12896 /**
12897 Returns the size of an event containing banks.
12898 The total size of an event is the value returned by bk_size() plus the size
12899 of the event header (sizeof(EVENT_HEADER)).
12900 @param event pointer to the area of event
12901 @return number of bytes contained in data area of event
12902 */
12903 INT bk_size(void *event)
12904 {
12905    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12906 }
12907 
12908 /********************************************************************/
12909 /**
12910 Create a Midas bank.
12911 The data pointer pdata must be used as an address to fill a
12912 bank. It is incremented with every value written to the bank and finally points
12913 to a location just after the last byte of the bank. It is then passed to
12914 the function bk_close() to finish the bank creation.
12915 \code
12916 INT *pdata;
12917 bk_init(pevent);
12918 bk_create(pevent, "ADC0", TID_INT, &pdata);
12919 *pdata++ = 123
12920 *pdata++ = 456
12921 bk_close(pevent, pdata);
12922 \endcode
12923 @param event pointer to the data area
12924 @param name of the bank, must be exactly 4 charaters
12925 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12926 midas.h
12927 @param pdata pointer to the data area of the newly created bank
12928 @return void
12929 */
12930 void bk_create(void *event, const char *name, WORD type, void *pdata)
12931 {
12932    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12933       BANK32 *pbk32;
12934 
12935       pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12936       strncpy(pbk32->name, name, 4);
12937       pbk32->type = type;
12938       pbk32->data_size = 0;
12939       *((void **) pdata) = pbk32 + 1;
12940    } else {
12941       BANK *pbk;
12942 
12943       pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12944       strncpy(pbk->name, name, 4);
12945       pbk->type = type;
12946       pbk->data_size = 0;
12947       *((void **) pdata) = pbk + 1;
12948    }
12949 }
12950 
12951 
12952 
12953 /**dox***************************************************************/
12954 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12955 
12956 /********************************************************************/
12957 int bk_delete(void *event, const char *name)
12958 /********************************************************************\
12959 
12960   Routine: bk_delete
12961 
12962   Purpose: Delete a MIDAS bank inside an event
12963 
12964   Input:
12965     void   *event           pointer to the event
12966     char   *name            Name of bank (exactly four letters)
12967 
12968   Function value:
12969     CM_SUCCESS              Bank has been deleted
12970     0                       Bank has not been found
12971 
12972 \********************************************************************/
12973 {
12974    BANK *pbk;
12975    BANK32 *pbk32;
12976    DWORD dname;
12977    int remaining;
12978 
12979    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12980       /* locate bank */
12981       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12982       strncpy((char *) &dname, name, 4);
12983       do {
12984          if (*((DWORD *) pbk32->name) == dname) {
12985             /* bank found, delete it */
12986             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
12987                          sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12988 
12989             /* reduce total event size */
12990             ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
12991 
12992             /* copy remaining bytes */
12993             if (remaining > 0)
12994                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12995             return CM_SUCCESS;
12996          }
12997 
12998          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12999       } while ((DWORD) ((char *) pbk32 - (char *) event) <
13000                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
13001    } else {
13002       /* locate bank */
13003       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
13004       strncpy((char *) &dname, name, 4);
13005       do {
13006          if (*((DWORD *) pbk->name) == dname) {
13007             /* bank found, delete it */
13008             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
13009                          sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13010 
13011             /* reduce total event size */
13012             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
13013 
13014             /* copy remaining bytes */
13015             if (remaining > 0)
13016                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
13017             return CM_SUCCESS;
13018          }
13019 
13020          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13021       } while ((DWORD) ((char *) pbk - (char *) event) <
13022                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
13023    }
13024 
13025    return 0;
13026 }
13027 
13028 /**dox***************************************************************/
13029 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13030 
13031 /********************************************************************/
13032 /**
13033 Close the Midas bank priviously created by bk_create().
13034 The data pointer pdata must be obtained by bk_create() and
13035 used as an address to fill a bank. It is incremented with every value written
13036 to the bank and finally points to a location just after the last byte of the
13037 bank. It is then passed to bk_close() to finish the bank creation
13038 @param event pointer to current composed event
13039 @param pdata  pointer to the data
13040 @return number of bytes contained in bank
13041 */
13042 INT bk_close(void *event, void *pdata)
13043 {
13044    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
13045       BANK32 *pbk32;
13046 
13047       pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
13048       pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
13049       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
13050          printf("Warning: bank %c%c%c%c has zero size\n",
13051                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
13052       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
13053       return pbk32->data_size;
13054    } else {
13055       BANK *pbk;
13056 
13057       pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
13058       pbk->data_size = (WORD) ((char *) pdata - (char *) (pbk + 1));
13059       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
13060          printf("Warning: bank %c%c%c%c has zero size\n", pbk->name[0], pbk->name[1], pbk->name[2],
13061                 pbk->name[3]);
13062       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
13063       return pbk->data_size;
13064    }
13065 }
13066 
13067 /********************************************************************/
13068 /**
13069 Extract the MIDAS bank name listing of an event.
13070 The bklist should be dimensioned with STRING_BANKLIST_MAX
13071 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
13072 \code
13073 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
13074 {
13075   INT    n_adc, nbanks;
13076   WORD   *pdata;
13077   char   banklist[STRING_BANKLIST_MAX];
13078 
13079   // Display # of banks and list of banks in the event
13080   nbanks = bk_list(pevent, banklist);
13081   printf("#banks:%d List:%s\n", nbanks, banklist);
13082 
13083   // look for ADC0 bank, return if not present
13084   n_adc = bk_locate(pevent, "ADC0", &pdata);
13085   ...
13086 }
13087 \endcode
13088 @param event pointer to current composed event
13089 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
13090 @return number of bank found in this event.
13091 */
13092 INT bk_list(void *event, char *bklist)
13093 {                               /* Full event */
13094    INT nbk, size;
13095    BANK *pmbk = NULL;
13096    BANK32 *pmbk32 = NULL;
13097    char *pdata;
13098 
13099    /* compose bank list */
13100    bklist[0] = 0;
13101    nbk = 0;
13102    do {
13103       /* scan all banks for bank name only */
13104       if (bk_is32(event)) {
13105          size = bk_iterate32(event, &pmbk32, &pdata);
13106          if (pmbk32 == NULL)
13107             break;
13108       } else {
13109          size = bk_iterate(event, &pmbk, &pdata);
13110          if (pmbk == NULL)
13111             break;
13112       }
13113       nbk++;
13114 
13115       if (nbk > BANKLIST_MAX) {
13116          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
13117          return (nbk - 1);
13118       }
13119       if (bk_is32(event))
13120          strncat(bklist, (char *) pmbk32->name, 4);
13121       else
13122          strncat(bklist, (char *) pmbk->name, 4);
13123    }
13124    while (1);
13125    return (nbk);
13126 }
13127 
13128 /********************************************************************/
13129 /**
13130 Locates a MIDAS bank of given name inside an event.
13131 @param event pointer to current composed event
13132 @param name bank name to look for
13133 @param pdata pointer to data area of bank, NULL if bank not found
13134 @return number of values inside the bank
13135 */
13136 INT bk_locate(void *event, const char *name, void *pdata)
13137 {
13138    BANK *pbk;
13139    BANK32 *pbk32;
13140    DWORD dname;
13141 
13142    if (bk_is32(event)) {
13143       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
13144       strncpy((char *) &dname, name, 4);
13145       while ((DWORD) ((char *) pbk32 - (char *) event) <
13146              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13147          if (*((DWORD *) pbk32->name) == dname) {
13148             *((void **) pdata) = pbk32 + 1;
13149             if (tid_size[pbk32->type & 0xFF] == 0)
13150                return pbk32->data_size;
13151             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
13152          }
13153          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13154       }
13155    } else {
13156       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
13157       strncpy((char *) &dname, name, 4);
13158       while ((DWORD) ((char *) pbk - (char *) event) <
13159              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13160          if (*((DWORD *) pbk->name) == dname) {
13161             *((void **) pdata) = pbk + 1;
13162             if (tid_size[pbk->type & 0xFF] == 0)
13163                return pbk->data_size;
13164             return pbk->data_size / tid_size[pbk->type & 0xFF];
13165          }
13166          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13167       }
13168 
13169    }
13170 
13171    /* bank not found */
13172    *((void **) pdata) = NULL;
13173    return 0;
13174 }
13175 
13176 /********************************************************************/
13177 /**
13178 Finds a MIDAS bank of given name inside an event.
13179 @param pbkh pointer to current composed event
13180 @param name bank name to look for
13181 @param bklen number of elemtents in bank
13182 @param bktype bank type, one of TID_xxx
13183 @param pdata pointer to data area of bank, NULL if bank not found
13184 @return 1 if bank found, 0 otherwise
13185 */
13186 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype, void **pdata)
13187 {
13188    BANK *pbk;
13189    BANK32 *pbk32;
13190    DWORD dname;
13191 
13192    if (bk_is32(pbkh)) {
13193       pbk32 = (BANK32 *) (pbkh + 1);
13194       strncpy((char *) &dname, name, 4);
13195       do {
13196          if (*((DWORD *) pbk32->name) == dname) {
13197             *((void **) pdata) = pbk32 + 1;
13198             if (tid_size[pbk32->type & 0xFF] == 0)
13199                *bklen = pbk32->data_size;
13200             else
13201                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
13202 
13203             *bktype = pbk32->type;
13204             return 1;
13205          }
13206          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13207       } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
13208    } else {
13209       pbk = (BANK *) (pbkh + 1);
13210       strncpy((char *) &dname, name, 4);
13211       do {
13212          if (*((DWORD *) pbk->name) == dname) {
13213             *((void **) pdata) = pbk + 1;
13214             if (tid_size[pbk->type & 0xFF] == 0)
13215                *bklen = pbk->data_size;
13216             else
13217                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
13218 
13219             *bktype = pbk->type;
13220             return 1;
13221          }
13222          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13223       } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
13224    }
13225 
13226    /* bank not found */
13227    *((void **) pdata) = NULL;
13228    return 0;
13229 }
13230 
13231 /********************************************************************/
13232 /**
13233 Iterates through banks inside an event.
13234 The function can be used to enumerate all banks of an event.
13235 The returned pointer to the bank header has following structure:
13236 \code
13237 typedef struct {
13238 char   name[4];
13239 WORD   type;
13240 WORD   data_size;
13241 } BANK;
13242 \endcode
13243 where type is a TID_xxx value and data_size the size of the bank in bytes.
13244 \code
13245 BANK *pbk;
13246 INT  size;
13247 void *pdata;
13248 char name[5];
13249 pbk = NULL;
13250 do
13251 {
13252  size = bk_iterate(event, &pbk, &pdata);
13253  if (pbk == NULL)
13254   break;
13255  *((DWORD *)name) = *((DWORD *)(pbk)->name);
13256  name[4] = 0;
13257  printf("bank %s found\n", name);
13258 } while(TRUE);
13259 \endcode
13260 @param event Pointer to data area of event.
13261 @param pbk pointer to the bank header, must be NULL for the first call to
13262 this function.
13263 @param pdata Pointer to the bank header, must be NULL for the first
13264 call to this function
13265 @return Size of bank in bytes
13266 */
13267 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
13268 {
13269    if (*pbk == NULL)
13270       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
13271    else
13272       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
13273 
13274    *((void **) pdata) = (*pbk) + 1;
13275 
13276    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13277       *pbk = *((BANK **) pdata) = NULL;
13278       return 0;
13279    }
13280 
13281    return (*pbk)->data_size;
13282 }
13283 
13284 
13285 /**dox***************************************************************/
13286 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13287 
13288 /********************************************************************/
13289 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
13290 /********************************************************************\
13291 
13292   Routine: bk_iterate
13293 
13294   Purpose: Iterate through 32 bit MIDAS banks inside an event
13295 
13296   Input:
13297     void   *event           pointer to the event
13298     BANK   **pbk32          must be NULL for the first call to bk_iterate
13299 
13300   Output:
13301     BANK   **pbk            pointer to the bank header
13302     void   *pdata           pointer to data area of the bank
13303 
13304   Function value:
13305     INT    size of the bank in bytes
13306 
13307 \********************************************************************/
13308 {
13309    if (*pbk == NULL)
13310       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
13311    else
13312       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
13313 
13314    *((void **) pdata) = (*pbk) + 1;
13315 
13316    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13317       *pbk = *((BANK32 **) pdata) = NULL;
13318       return 0;
13319    }
13320 
13321    return (*pbk)->data_size;
13322 }
13323 
13324 /**dox***************************************************************/
13325 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13326 
13327 /********************************************************************/
13328 /**
13329 Swaps bytes from little endian to big endian or vice versa for a whole event.
13330 
13331 An event contains a flag which is set by bk_init() to identify
13332 the endian format of an event. If force is FALSE, this flag is evaluated and
13333 the event is only swapped if it is in the "wrong" format for this system.
13334 An event can be swapped to the "wrong" format on purpose for example by a
13335 front-end which wants to produce events in a "right" format for a back-end
13336 analyzer which has different byte ordering.
13337 @param event pointer to data area of event
13338 @param force If TRUE, the event is always swapped, if FALSE, the event
13339 is only swapped if it is in the wrong format.
13340 @return 1==event has been swap, 0==event has not been swapped.
13341 */
13342 INT bk_swap(void *event, BOOL force)
13343 {
13344    BANK_HEADER *pbh;
13345    BANK *pbk;
13346    BANK32 *pbk32;
13347    void *pdata;
13348    WORD type;
13349    BOOL b32;
13350 
13351    pbh = (BANK_HEADER *) event;
13352 
13353    /* only swap if flags in high 16-bit */
13354    if (pbh->flags < 0x10000 && !force)
13355       return 0;
13356 
13357    /* swap bank header */
13358    DWORD_SWAP(&pbh->data_size);
13359    DWORD_SWAP(&pbh->flags);
13360 
13361    /* check for 32bit banks */
13362    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
13363 
13364    pbk = (BANK *) (pbh + 1);
13365    pbk32 = (BANK32 *) pbk;
13366 
13367    /* scan event */
13368    while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
13369       /* swap bank header */
13370       if (b32) {
13371          DWORD_SWAP(&pbk32->type);
13372          DWORD_SWAP(&pbk32->data_size);
13373          pdata = pbk32 + 1;
13374          type = (WORD) pbk32->type;
13375       } else {
13376          WORD_SWAP(&pbk->type);
13377          WORD_SWAP(&pbk->data_size);
13378          pdata = pbk + 1;
13379          type = pbk->type;
13380       }
13381 
13382       /* pbk points to next bank */
13383       if (b32) {
13384          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13385          pbk = (BANK *) pbk32;
13386       } else {
13387          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13388          pbk32 = (BANK32 *) pbk;
13389       }
13390 
13391       switch (type) {
13392       case TID_WORD:
13393       case TID_SHORT:
13394          while ((char *) pdata < (char *) pbk) {
13395             WORD_SWAP(pdata);
13396             pdata = (void *) (((WORD *) pdata) + 1);
13397          }
13398          break;
13399 
13400       case TID_DWORD:
13401       case TID_INT:
13402       case TID_BOOL:
13403       case TID_FLOAT:
13404          while ((char *) pdata < (char *) pbk) {
13405             DWORD_SWAP(pdata);
13406             pdata = (void *) (((DWORD *) pdata) + 1);
13407          }
13408          break;
13409 
13410       case TID_DOUBLE:
13411          while ((char *) pdata < (char *) pbk) {
13412             QWORD_SWAP(pdata);
13413             pdata = (void *) (((double *) pdata) + 1);
13414          }
13415          break;
13416       }
13417    }
13418 
13419    return CM_SUCCESS;
13420 }
13421 
13422 /**dox***************************************************************/
13423 
13424 /** @} *//* end of bkfunctionc */
13425 
13426 
13427 /**dox***************************************************************/
13428 /** @addtogroup rbfunctionc
13429  *
13430  *  @{  */
13431 
13432 /**dox***************************************************************/
13433 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13434 /********************************************************************/
13435 
13436 /********************************************************************\
13437 *                                                                    *
13438 *                 Ring buffer functions                              *
13439 *                                                                    *
13440 * Provide an inter-thread buffer scheme for handling front-end       *
13441 * events. This code allows concurrent data acquisition, calibration  *
13442 * and network transfer on a multi-CPU machine. One thread reads      *
13443 * out the data, passes it vis the ring buffer functions              *
13444 * to another thread running on the other CPU, which can then         *
13445 * calibrate and/or send the data over the network.                   *
13446 *                                                                    *
13447 \********************************************************************/
13448 
13449 typedef struct {
13450    unsigned char *buffer;
13451    unsigned int size;
13452    unsigned int max_event_size;
13453    unsigned char *rp;
13454    unsigned char *wp;
13455    unsigned char *ep;
13456 } RING_BUFFER;
13457 
13458 #define MAX_RING_BUFFER 100
13459 
13460 RING_BUFFER rb[MAX_RING_BUFFER];
13461 
13462 volatile int _rb_nonblocking = 0;
13463 
13464 /**dox***************************************************************/
13465 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13466 
13467 /********************************************************************/
13468 /**
13469 Set all rb_get_xx to nonblocking. Needed in multi-thread
13470 environments for stopping all theads without deadlock
13471 @return DB_SUCCESS
13472 */
13473 int rb_set_nonblocking()
13474 /********************************************************************\
13475 
13476   Routine: rb_set_nonblocking
13477 
13478   Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
13479            environments for stopping all theads without deadlock
13480 
13481   Input:
13482     NONE
13483 
13484   Output:
13485     NONE
13486 
13487   Function value:
13488     DB_SUCCESS       Successful completion
13489 
13490 \********************************************************************/
13491 {
13492    _rb_nonblocking = 1;
13493 
13494    return DB_SUCCESS;
13495 }
13496 
13497 /********************************************************************/
13498 /**
13499 Create a ring buffer with a given size
13500 
13501 Provide an inter-thread buffer scheme for handling front-end
13502 events. This code allows concurrent data acquisition, calibration
13503 and network transfer on a multi-CPU machine. One thread reads
13504 out the data, passes it via the ring buffer functions
13505 to another thread running on the other CPU, which can then
13506 calibrate and/or send the data over the network.
13507 
13508 @param size             Size of ring buffer, must be larger than
13509                          2*max_event_size
13510 @param max_event_size   Maximum event size to be placed into
13511 @param *handle          Handle to ring buffer
13512 @return DB_SUCCESS, DB_NO_MEMORY, DB_INVALID_PARAM
13513 */
13514 int rb_create(int size, int max_event_size, int *handle)
13515 /********************************************************************\
13516 
13517   Routine: rb_create
13518 
13519   Purpose: Create a ring buffer with a given size
13520 
13521   Input:
13522     int size             Size of ring buffer, must be larger than
13523                          2*max_event_size
13524     int max_event_size   Maximum event size to be placed into
13525                          ring buffer
13526   Output:
13527     int *handle          Handle to ring buffer
13528 
13529   Function value:
13530     DB_SUCCESS           Successful completion
13531     DB_NO_MEMORY         Maximum number of ring buffers exceeded
13532     DB_INVALID_PARAM     Invalid event size specified
13533 
13534 \********************************************************************/
13535 {
13536    int i;
13537 
13538    for (i = 0; i < MAX_RING_BUFFER; i++)
13539       if (rb[i].buffer == NULL)
13540          break;
13541 
13542    if (i == MAX_RING_BUFFER)
13543       return DB_NO_MEMORY;
13544 
13545    if (size < max_event_size * 2)
13546       return DB_INVALID_PARAM;
13547 
13548    memset(&rb[i], 0, sizeof(RING_BUFFER));
13549    rb[i].buffer = (unsigned char *) M_MALLOC(size);
13550    assert(rb[i].buffer);
13551    rb[i].size = size;
13552    rb[i].max_event_size = max_event_size;
13553    rb[i].rp = rb[i].buffer;
13554    rb[i].wp = rb[i].buffer;
13555    rb[i].ep = rb[i].buffer;
13556 
13557    *handle = i + 1;
13558 
13559    return DB_SUCCESS;
13560 }
13561 
13562 /********************************************************************/
13563 /**
13564 Delete a ring buffer
13565 @param handle  Handle of the ring buffer
13566 @return  DB_SUCCESS
13567 */
13568 int rb_delete(int handle)
13569 /********************************************************************\
13570 
13571   Routine: rb_delete
13572 
13573   Purpose: Delete a ring buffer
13574 
13575   Input:
13576     none
13577   Output:
13578     int handle       Handle to ring buffer
13579 
13580   Function value:
13581     DB_SUCCESS       Successful completion
13582 
13583 \********************************************************************/
13584 {
13585    if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
13586       return DB_INVALID_HANDLE;
13587 
13588    M_FREE(rb[handle - 1].buffer);
13589    memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
13590 
13591    return DB_SUCCESS;
13592 }
13593 
13594 /********************************************************************/
13595 /**
13596 Retrieve write pointer where new data can be written
13597 @param handle               Ring buffer handle
13598 @param millisec             Optional timeout in milliseconds if
13599                               buffer is full. Zero to not wait at
13600                               all (non-blocking)
13601 @param  **p                  Write pointer
13602 @return DB_SUCCESS, DB_TIMEOUT, DB_INVALID_HANDLE
13603 */
13604 int rb_get_wp(int handle, void **p, int millisec)
13605 /********************************************************************\
13606 
13607 Routine: rb_get_wp
13608 
13609   Purpose: Retrieve write pointer where new data can be written
13610 
13611   Input:
13612      int handle               Ring buffer handle
13613      int millisec             Optional timeout in milliseconds if
13614                               buffer is full. Zero to not wait at
13615                               all (non-blocking)
13616 
13617   Output:
13618     char **p                  Write pointer
13619 
13620   Function value:
13621     DB_SUCCESS       Successful completion
13622 
13623 \********************************************************************/
13624 {
13625    int h, i;
13626    unsigned char *rp;
13627 
13628    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
13629       return DB_INVALID_HANDLE;
13630 
13631    h = handle - 1;
13632 
13633    for (i = 0; i <= millisec / 10; i++) {
13634 
13635       rp = rb[h].rp;            // keep local copy, rb[h].rp might be changed by other thread
13636 
13637       /* check if enough size for wp >= rp without wrap-around */
13638       if (rb[h].wp >= rp
13639           && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
13640          *p = rb[h].wp;
13641          return DB_SUCCESS;
13642       }
13643 
13644       /* check if enough size for wp >= rp with wrap-around */
13645       if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size && rb[h].rp > rb[h].buffer) {    // next increment of wp wraps around, so need space at beginning
13646          *p = rb[h].wp;
13647          return DB_SUCCESS;
13648       }
13649 
13650       /* check if enough size for wp < rp */
13651       if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
13652          *p = rb[h].wp;
13653          return DB_SUCCESS;
13654       }
13655 
13656       if (millisec == 0)
13657          return DB_TIMEOUT;
13658 
13659       if (_rb_nonblocking)
13660          return DB_TIMEOUT;
13661 
13662       /* wait one time slice */
13663       ss_sleep(10);
13664    }
13665 
13666    return DB_TIMEOUT;
13667 }
13668 
13669 /********************************************************************/
13670 /** rb_increment_wp
13671 
13672 Increment current write pointer, making the data at
13673 the write pointer available to the receiving thread
13674 @param handle               Ring buffer handle
13675 @param size                 Number of bytes placed at the WP
13676 @return DB_SUCCESS, DB_INVALID_PARAM, DB_INVALID_HANDLE
13677 */
13678 int rb_increment_wp(int handle, int size)
13679 /********************************************************************\
13680 
13681   Routine: rb_increment_wp
13682 
13683   Purpose: Increment current write pointer, making the data at
13684            the write pointer available to the receiving thread
13685 
13686   Input:
13687      int handle               Ring buffer handle
13688      int size                 Number of bytes placed at the WP
13689 
13690   Output:
13691     NONE
13692 
13693   Function value:
13694     DB_SUCCESS                Successful completion
13695     DB_INVALID_PARAM          Event size too large or invalid handle
13696 \********************************************************************/
13697 {
13698    int h;
13699    unsigned char *old_wp, *new_wp;
13700 
13701    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
13702       return DB_INVALID_HANDLE;
13703 
13704    h = handle - 1;
13705 
13706    if ((DWORD) size > rb[h].max_event_size)
13707       return DB_INVALID_PARAM;
13708 
13709    old_wp = rb[h].wp;
13710    new_wp = rb[h].wp + size;
13711 
13712    /* wrap around wp if not enough space */
13713    if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
13714       rb[h].ep = new_wp;
13715       new_wp = rb[h].buffer;
13716       assert(rb[h].rp != rb[h].buffer);
13717    }
13718 
13719    rb[h].wp = new_wp;
13720 
13721    return DB_SUCCESS;
13722 }
13723 
13724 /********************************************************************/
13725 /**
13726 Obtain the current read pointer at which new data is
13727 available with optional timeout
13728 
13729 @param  handle               Ring buffer handle
13730 @param  millisec             Optional timeout in milliseconds if
13731                              buffer is full. Zero to not wait at
13732                              all (non-blocking)
13733 
13734 @param **p                 Address of pointer pointing to newly
13735                              available data. If p == NULL, only
13736                              return status.
13737 @return  DB_SUCCESS, DB_TIEMOUT, DB_INVALID_HANDLE
13738 
13739 */
13740 int rb_get_rp(int handle, void **p, int millisec)
13741 /********************************************************************\
13742 
13743   Routine: rb_get_rp
13744 
13745   Purpose: Obtain the current read pointer at which new data is
13746            available with optional timeout
13747 
13748   Input:
13749     int handle               Ring buffer handle
13750     int millisec             Optional timeout in milliseconds if
13751                              buffer is full. Zero to not wait at
13752                              all (non-blocking)
13753 
13754   Output:
13755     char **p                 Address of pointer pointing to newly
13756                              available data. If p == NULL, only
13757                              return status.
13758 
13759   Function value:
13760     DB_SUCCESS       Successful completion
13761 
13762 \********************************************************************/
13763 {
13764    int i, h;
13765 
13766    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
13767       return DB_INVALID_HANDLE;
13768 
13769    h = handle - 1;
13770 
13771    for (i = 0; i <= millisec / 10; i++) {
13772 
13773       if (rb[h].wp != rb[h].rp) {
13774          if (p != NULL)
13775             *p = rb[handle - 1].rp;
13776          return DB_SUCCESS;
13777       }
13778 
13779       if (millisec == 0)
13780          return DB_TIMEOUT;
13781 
13782       if (_rb_nonblocking)
13783          return DB_TIMEOUT;
13784 
13785       /* wait one time slice */
13786       ss_sleep(10);
13787    }
13788 
13789    return DB_TIMEOUT;
13790 }
13791 
13792 /********************************************************************/
13793 /**
13794 Increment current read pointer, freeing up space for the writing thread.
13795 
13796 @param handle               Ring buffer handle
13797 @param size                 Number of bytes to free up at current
13798                               read pointer
13799 @return  DB_SUCCESS, DB_INVALID_PARAM
13800 
13801 */
13802 int rb_increment_rp(int handle, int size)
13803 /********************************************************************\
13804 
13805   Routine: rb_increment_rp
13806 
13807   Purpose: Increment current read pointer, freeing up space for
13808            the writing thread.
13809 
13810   Input:
13811      int handle               Ring buffer handle
13812      int size                 Number of bytes to free up at current
13813                               read pointer
13814 
13815   Output:
13816     NONE
13817 
13818   Function value:
13819     DB_SUCCESS                Successful completion
13820     DB_INVALID_PARAM          Event size too large or invalid handle
13821 
13822 \********************************************************************/
13823 {
13824    int h;
13825 
13826    unsigned char *new_rp, *old_rp;
13827 
13828    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
13829       return DB_INVALID_HANDLE;
13830 
13831    h = handle - 1;
13832 
13833    if ((DWORD) size > rb[h].max_event_size)
13834       return DB_INVALID_PARAM;
13835 
13836    old_rp = rb[h].rp;
13837    new_rp = rb[h].rp + size;
13838 
13839    /* wrap around if not enough space left */
13840    if (new_rp + rb[h].max_event_size > rb[h].buffer + rb[h].size)
13841       new_rp = rb[h].buffer;
13842 
13843    rb[handle - 1].rp = new_rp;
13844 
13845    return DB_SUCCESS;
13846 }
13847 
13848 /********************************************************************/
13849 /**
13850 Return number of bytes in a ring buffer
13851 
13852 @param handle              Handle of the buffer to get the info
13853 @param *n_bytes            Number of bytes in buffer
13854 @return DB_SUCCESS, DB_INVALID_HANDLE
13855 */
13856 int rb_get_buffer_level(int handle, int *n_bytes)
13857 /********************************************************************\
13858 
13859   Routine: rb_get_buffer_level
13860 
13861   Purpose: Return number of bytes in a ring buffer
13862 
13863   Input:
13864     int handle              Handle of the buffer to get the info
13865 
13866   Output:
13867     int *n_bytes            Number of bytes in buffer
13868 
13869   Function value:
13870     DB_SUCCESS              Successful completion
13871     DB_INVALID_HANDLE       Buffer handle is invalid
13872 
13873 \********************************************************************/
13874 {
13875    int h;
13876 
13877    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
13878       return DB_INVALID_HANDLE;
13879 
13880    h = handle - 1;
13881 
13882    if (rb[h].wp >= rb[h].rp)
13883       *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
13884    else
13885       *n_bytes =
13886           (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
13887 
13888    return DB_SUCCESS;
13889 }
13890 
13891 /**dox***************************************************************/
13892                   /** @} *//* end of rbfunctionc */
13893 
13894 /**dox***************************************************************/
13895                   /** @} *//* end of midasincludecode */

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