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:$
00009 
00010 \********************************************************************/
00011 
00012 #include "midas.h"
00013 #include "msystem.h"
00014 #include <assert.h>
00015 
00016 /**dox***************************************************************/
00017 /** @file midas.c
00018 The main core C-code for Midas.
00019 */
00020 
00021 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00022  */
00023 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00024  */
00025 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00026  */
00027 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00028  */
00029 /** @defgroup alfunctionc Midas Alarm Functions (al_xxx)
00030  */
00031 /** @defgroup hsfunctionc Midas History Functions (hs_xxx)
00032  */
00033 /** @defgroup elfunctionc Midas Elog Functions (el_xxx)
00034  */
00035 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00036  */
00037 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00038  */
00039 
00040 /**dox***************************************************************/
00041 /** @addtogroup midasincludecode
00042  *  
00043  *  @{  */
00044 
00045 /**dox***************************************************************/
00046 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00047 
00048 /********************************************************************/
00049 /* data type sizes */
00050 INT tid_size[] = {
00051    0,                           /* tid == 0 not defined                               */
00052    1,                           /* TID_BYTE      unsigned byte         0       255    */
00053    1,                           /* TID_SBYTE     signed byte         -128      127    */
00054    1,                           /* TID_CHAR      single character      0       255    */
00055    2,                           /* TID_WORD      two bytes             0      65535   */
00056    2,                           /* TID_SHORT     signed word        -32768    32767   */
00057    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00058    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00059    4,                           /* TID_BOOL      four bytes bool       0        1     */
00060    4,                           /* TID_FLOAT     4 Byte float format                  */
00061    8,                           /* TID_DOUBLE    8 Byte float format                  */
00062    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00063    0,                           /* TID_STRING    zero terminated string               */
00064    0,                           /* TID_ARRAY     variable length array of unkown type */
00065    0,                           /* TID_STRUCT    C structure                          */
00066    0,                           /* TID_KEY       key in online database               */
00067    0                            /* TID_LINK      link in online database              */
00068 };
00069 
00070 /* data type names */
00071 char *tid_name[] = {
00072    "NULL",
00073    "BYTE",
00074    "SBYTE",
00075    "CHAR",
00076    "WORD",
00077    "SHORT",
00078    "DWORD",
00079    "INT",
00080    "BOOL",
00081    "FLOAT",
00082    "DOUBLE",
00083    "BITFIELD",
00084    "STRING",
00085    "ARRAY",
00086    "STRUCT",
00087    "KEY",
00088    "LINK"
00089 };
00090 
00091 struct {
00092    int transition;
00093    char name[32];
00094 } trans_name[] = {
00095    {
00096    TR_START, "START",}, {
00097    TR_STOP, "STOP",}, {
00098    TR_PAUSE, "PAUSE",}, {
00099    TR_RESUME, "RESUME",}, {
00100    TR_DEFERRED, "DEFERRED",}, {
00101    0, "",},};
00102 
00103 /* Globals */
00104 #ifdef OS_MSDOS
00105 extern unsigned _stklen = 60000U;
00106 #endif
00107 
00108 extern DATABASE *_database;
00109 extern INT _database_entries;
00110 
00111 static BUFFER *_buffer;
00112 static INT _buffer_entries = 0;
00113 
00114 static INT _msg_buffer = 0;
00115 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00116 
00117 static REQUEST_LIST *_request_list;
00118 static INT _request_list_entries = 0;
00119 
00120 static EVENT_HEADER *_event_buffer;
00121 static INT _event_buffer_size = 0;
00122 
00123 static char *_net_recv_buffer;
00124 static INT _net_recv_buffer_size = 0;
00125 
00126 static char *_net_send_buffer;
00127 static INT _net_send_buffer_size = 0;
00128 
00129 static char *_tcp_buffer = NULL;
00130 static INT _tcp_wp = 0;
00131 static INT _tcp_rp = 0;
00132 
00133 static INT _send_sock;
00134 
00135 static void (*_debug_print) (char *) = NULL;
00136 static INT _debug_mode = 0;
00137 
00138 static INT _watchdog_last_called = 0;
00139 
00140 /* table for transition functions */
00141 
00142 typedef struct {
00143    INT transition;
00144    INT sequence_number;
00145     INT(*func) (INT, char *);
00146 } TRANS_TABLE;
00147 
00148 #define MAX_TRANSITIONS 20
00149 
00150 TRANS_TABLE _trans_table[MAX_TRANSITIONS];
00151 
00152 TRANS_TABLE _deferred_trans_table[] = {
00153    {TR_START},
00154    {TR_STOP},
00155    {TR_PAUSE},
00156    {TR_RESUME},
00157    {0}
00158 };
00159 
00160 static BOOL _server_registered = FALSE;
00161 
00162 static INT rpc_transition_dispatch(INT index, void *prpc_param[]);
00163 
00164 void cm_ctrlc_handler(int sig);
00165 
00166 typedef struct {
00167    INT code;
00168    char *string;
00169 } ERROR_TABLE;
00170 
00171 ERROR_TABLE _error_table[] = {
00172    {CM_WRONG_PASSWORD, "Wrong password"},
00173    {CM_UNDEF_EXP, "Experiment not defined"},
00174    {CM_UNDEF_ENVIRON,
00175     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00176    {RPC_NET_ERROR, "Cannot connect to remote host"},
00177    {0, NULL}
00178 };
00179 
00180 
00181 typedef struct {
00182    void *adr;
00183    int size;
00184    char file[80];
00185    int line;
00186 } DBG_MEM_LOC;
00187 
00188 DBG_MEM_LOC *_mem_loc = NULL;
00189 INT _n_mem = 0;
00190 
00191 void *dbg_malloc(unsigned int size, char *file, int line)
00192 {
00193    FILE *f;
00194    void *adr;
00195    int i;
00196 
00197    adr = malloc(size);
00198 
00199    /* search for deleted entry */
00200    for (i = 0; i < _n_mem; i++)
00201       if (_mem_loc[i].adr == NULL)
00202          break;
00203 
00204    if (i == _n_mem) {
00205       _n_mem++;
00206       if (!_mem_loc)
00207          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00208       else
00209          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00210    }
00211 
00212    _mem_loc[i].adr = adr;
00213    _mem_loc[i].size = size;
00214    strcpy(_mem_loc[i].file, file);
00215    _mem_loc[i].line = line;
00216 
00217    f = fopen("mem.txt", "w");
00218    for (i = 0; i < _n_mem; i++)
00219       if (_mem_loc[i].adr)
00220          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00221                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00222    fclose(f);
00223 
00224    return adr;
00225 }
00226 
00227 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00228 {
00229    void *adr;
00230 
00231    adr = dbg_malloc(size * count, file, line);
00232    if (adr)
00233       memset(adr, 0, size * count);
00234 
00235    return adr;
00236 }
00237 
00238 void dbg_free(void *adr, char *file, int line)
00239 {
00240    FILE *f;
00241    int i;
00242 
00243    free(adr);
00244 
00245    for (i = 0; i < _n_mem; i++)
00246       if (_mem_loc[i].adr == adr)
00247          break;
00248 
00249    if (i < _n_mem)
00250       _mem_loc[i].adr = NULL;
00251 
00252    f = fopen("mem.txt", "w");
00253    for (i = 0; i < _n_mem; i++)
00254       if (_mem_loc[i].adr)
00255          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00256                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00257    fclose(f);
00258 
00259 }
00260 
00261 /********************************************************************\
00262 *                                                                    *
00263 *              Common message functions                              *
00264 *                                                                    *
00265 \********************************************************************/
00266 
00267 static int (*_message_print) (const char *) = puts;
00268 static INT _message_mask_system = MT_ALL;
00269 static INT _message_mask_user = MT_ALL;
00270 
00271 
00272 /**dox***************************************************************/
00273 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00274 
00275 /**dox***************************************************************/
00276 /** @addtogroup msgfunctionc
00277  *  
00278  *  @{  */
00279 
00280 /********************************************************************/
00281 /**
00282 Convert error code to string. Used after cm_connect_experiment to print
00283 error string in command line programs or windows programs.
00284 @param code Error code as defined in midas.h
00285 @param string Error string
00286 @return CM_SUCCESS
00287 */
00288 INT cm_get_error(INT code, char *string)
00289 {
00290    INT i;
00291 
00292    for (i = 0; _error_table[i].code; i++)
00293       if (_error_table[i].code == code) {
00294          strcpy(string, _error_table[i].string);
00295          return CM_SUCCESS;
00296       }
00297 
00298    sprintf(string, "Unexpected error #%d", code);
00299    return CM_SUCCESS;
00300 }
00301 
00302 /********************************************************************/
00303 /** 
00304 Set message masks. When a message is generated by calling cm_msg(),
00305 it can got to two destinatinons. First a user defined callback routine
00306 and second to the "SYSMSG" buffer.
00307 
00308 A user defined callback receives all messages which satisfy the user_mask.
00309 
00310 \code
00311 int message_print(const char *msg)
00312 {
00313   char str[160];
00314 
00315   memset(str, ' ', 159);
00316   str[159] = 0;
00317   if (msg[0] == '[')
00318     msg = strchr(msg, ']')+2;
00319   memcpy(str, msg, strlen(msg));
00320   ss_printf(0, 20, str);
00321   return 0;
00322 }
00323 ...
00324   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
00325 ...
00326 \endcode
00327 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
00328 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
00329 @param func Function which receives all printout. By setting "puts",
00330        messages are just printed to the screen.
00331 @return CM_SUCCESS
00332 */
00333 INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func) (const char *))
00334 {
00335    _message_mask_system = system_mask;
00336    _message_mask_user = user_mask;
00337    _message_print = func;
00338 
00339    return BM_SUCCESS;
00340 }
00341 
00342 /********************************************************************/
00343 /**
00344 Write message to logging file. Called by cm_msg.
00345 @attention May burn your fingers
00346 @param message_type      Message type
00347 @param message          Message string
00348 @return CM_SUCCESS
00349 */
00350 INT cm_msg_log(INT message_type, const char *message)
00351 {
00352    char dir[256];
00353    char filename[256];
00354    char path[256];
00355    char str[256];
00356    INT status, size, fh;
00357    HNDLE hDB, hKey;
00358 
00359    if (rpc_is_remote())
00360       return rpc_call(RPC_CM_MSG_LOG, message_type, message);
00361 
00362    if (message_type != MT_DEBUG) {
00363       cm_get_experiment_database(&hDB, NULL);
00364 
00365       if (hDB) {
00366          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00367          if (status == DB_SUCCESS) {
00368             size = sizeof(dir);
00369             memset(dir, 0, size);
00370             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00371             if (dir[0] != 0)
00372                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00373                   strcat(dir, DIR_SEPARATOR_STR);
00374 
00375             strcpy(filename, "midas.log");
00376             size = sizeof(filename);
00377             db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
00378                          TRUE);
00379 
00380             strcpy(path, dir);
00381             strcat(path, filename);
00382          } else {
00383             cm_get_path(dir);
00384             if (dir[0] != 0)
00385                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00386                   strcat(dir, DIR_SEPARATOR_STR);
00387 
00388             strcpy(path, dir);
00389             strcat(path, "midas.log");
00390          }
00391       } else
00392          strcpy(path, "midas.log");
00393 
00394       fh = open(path, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
00395       if (fh < 0) {
00396          printf("Cannot open message log file %s\n", path);
00397       } else {
00398          strcpy(str, ss_asctime());
00399          write(fh, str, strlen(str));
00400          write(fh, " ", 1);
00401          write(fh, message, strlen(message));
00402          write(fh, "\n", 1);
00403          close(fh);
00404       }
00405    }
00406 
00407    return CM_SUCCESS;
00408 }
00409 
00410 /********************************************************************/
00411 /**
00412 Write message to logging file. Called by cm_msg().
00413 @internal 
00414 @param message_type      Message type
00415 @param message          Message string
00416 @param facility         Message facility, filename in which messages will be written
00417 @return CM_SUCCESS
00418 */
00419 INT cm_msg_log1(INT message_type, const char *message, const char *facility)
00420 /********************************************************************\
00421 
00422   Routine: cm_msg_log1
00423 
00424   Purpose: Write message to logging file. Called by cm_msg.
00425            Internal use only
00426 
00427   Input:
00428     INT    message_type      Message type
00429     char   *message          Message string
00430     char   *
00431 
00432   Output:
00433     none
00434 
00435   Function value:
00436     CM_SUCCESS
00437 
00438 \********************************************************************/
00439 {
00440    char dir[256];
00441    char filename[256];
00442    char path[256];
00443    char str[256];
00444    FILE *f;
00445    INT status, size;
00446    HNDLE hDB, hKey;
00447 
00448 
00449    if (rpc_is_remote())
00450       return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
00451 
00452    if (message_type != MT_DEBUG) {
00453       cm_get_experiment_database(&hDB, NULL);
00454 
00455       if (hDB) {
00456          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00457          if (status == DB_SUCCESS) {
00458             size = sizeof(dir);
00459             memset(dir, 0, size);
00460             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00461             if (dir[0] != 0)
00462                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00463                   strcat(dir, DIR_SEPARATOR_STR);
00464 
00465             if (facility[0]) {
00466                strcpy(filename, facility);
00467                strcat(filename, ".log");
00468             } else {
00469                strcpy(filename, "midas.log");
00470                size = sizeof(filename);
00471                db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
00472                             TRUE);
00473             }
00474 
00475             strcpy(path, dir);
00476             strcat(path, filename);
00477          } else {
00478             cm_get_path(dir);
00479             if (dir[0] != 0)
00480                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00481                   strcat(dir, DIR_SEPARATOR_STR);
00482 
00483             strcpy(path, dir);
00484             if (facility[0]) {
00485                strcat(path, facility);
00486                strcat(path, ".log");
00487             } else
00488                strcat(path, "midas.log");
00489          }
00490       } else {
00491          if (facility[0]) {
00492             strcpy(path, facility);
00493             strcat(path, ".log");
00494          } else
00495             strcpy(path, "midas.log");
00496       }
00497 
00498       f = fopen(path, "a");
00499       if (f == NULL) {
00500          printf("Cannot open message log file %s\n", path);
00501       } else {
00502          strcpy(str, ss_asctime());
00503          fprintf(f, str);
00504          fprintf(f, " %s\n", message);
00505          fclose(f);
00506       }
00507    }
00508 
00509    return CM_SUCCESS;
00510 }
00511 
00512 /********************************************************************/
00513 /** 
00514 This routine can be called whenever an internal error occurs
00515 or an informative message is produced. Different message
00516 types can be enabled or disabled by setting the type bits
00517 via cm_set_msg_print().
00518 @attention Do not add the "\n" escape carriage control at the end of the
00519 formated line as it is already added by the client on the receiving side.
00520 \code
00521    ...
00522    cm_msg(MINFO, "my program", "This is a information message only);
00523    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
00524    cm_msg(MTALK, "my_program", My program is Done!");
00525    ...
00526 \endcode
00527 @param message_type (See @ref midas_macro).
00528 @param filename Name of source file where error occured
00529 @param line Line number where error occured
00530 @param routine Routine name.
00531 @param format message to printout, ... Parameters like for printf()
00532 @return CM_SUCCESS
00533 */
00534 INT cm_msg(INT message_type, char *filename, INT line,
00535            const char *routine, const char *format, ...)
00536 {
00537    va_list argptr;
00538    char event[1000], str[256], local_message[256], send_message[256], *pc;
00539    EVENT_HEADER *pevent;
00540    INT status;
00541    static BOOL in_routine = FALSE;
00542 
00543    /* avoid recursive calls */
00544    if (in_routine)
00545       return 0;
00546 
00547    in_routine = TRUE;
00548 
00549    /* strip path */
00550    pc = filename + strlen(filename);
00551    while (*pc != '\\' && *pc != '/' && pc != filename)
00552       pc--;
00553    if (pc != filename)
00554       pc++;
00555 
00556    /* print client name into string */
00557    if (message_type == MT_USER)
00558       sprintf(send_message, "[%s] ", routine);
00559    else {
00560       rpc_get_name(str);
00561       if (str[0])
00562          sprintf(send_message, "[%s] ", str);
00563       else
00564          send_message[0] = 0;
00565    }
00566 
00567    local_message[0] = 0;
00568 
00569    /* preceed error messages with file and line info */
00570    if (message_type == MT_ERROR) {
00571       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
00572       strcat(send_message, str);
00573       strcat(local_message, str);
00574    }
00575 
00576    /* print argument list into message */
00577    va_start(argptr, format);
00578    vsprintf(str, (char *) format, argptr);
00579    va_end(argptr);
00580    strcat(send_message, str);
00581    strcat(local_message, str);
00582 
00583    /* call user function if set via cm_set_msg_print */
00584    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00585       _message_print(local_message);
00586 
00587    /* return if system mask is not set */
00588    if ((message_type & _message_mask_system) == 0) {
00589       in_routine = FALSE;
00590       return CM_SUCCESS;
00591    }
00592 
00593    /* copy message to event */
00594    pevent = (EVENT_HEADER *) event;
00595    strcpy(event + sizeof(EVENT_HEADER), send_message);
00596 
00597    /* send event if not of type MLOG */
00598    if (message_type != MT_LOG) {
00599       /* if no message buffer already opened, do so now */
00600       if (_msg_buffer == 0) {
00601          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00602          if (status != BM_SUCCESS && status != BM_CREATED) {
00603             in_routine = FALSE;
00604             return status;
00605          }
00606       }
00607 
00608       /* setup the event header and send the message */
00609       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00610                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00611       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00612    }
00613 
00614    /* log message */
00615    cm_msg_log(message_type, send_message);
00616 
00617    in_routine = FALSE;
00618 
00619    return CM_SUCCESS;
00620 }
00621 
00622 /********************************************************************/
00623 /**
00624 This routine is similar to @ref cm_msg().
00625 It differs from cm_msg() only by the logging destination being a file
00626 given through the argument list i.e:\b facility
00627 @internal
00628 @attention Do not add the "\n" escape carriage control at the end of the
00629 formated line as it is already added by the client on the receiving side.
00630 The first arg in the following example uses the predefined
00631 macro MINFO which handles automatically the first 3 arguments of the function
00632 (see @ref midas_macro).
00633 \code   ...
00634    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
00635    ...
00636 //----- File my_log_file.log
00637 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
00638 \endcode
00639 @param message_type See @ref midas_macro.
00640 @param filename Name of source file where error occured
00641 @param line Line number where error occured
00642 @param facility Logging file name
00643 @param routine Routine name
00644 @param format message to printout, ... Parameters like for printf()
00645 @return CM_SUCCESS
00646 */
00647 INT cm_msg1(INT message_type, char *filename, INT line,
00648             const char *facility, const char *routine, const char *format, ...)
00649 {
00650    va_list argptr;
00651    char event[1000], str[256], local_message[256], send_message[256], *pc;
00652    EVENT_HEADER *pevent;
00653    INT status;
00654    static BOOL in_routine = FALSE;
00655 
00656    /* avoid recursive calles */
00657    if (in_routine)
00658       return 0;
00659 
00660    in_routine = TRUE;
00661 
00662    /* strip path */
00663    pc = filename + strlen(filename);
00664    while (*pc != '\\' && *pc != '/' && pc != filename)
00665       pc--;
00666    if (pc != filename)
00667       pc++;
00668 
00669    /* print client name into string */
00670    if (message_type == MT_USER)
00671       sprintf(send_message, "[%s] ", routine);
00672    else {
00673       rpc_get_name(str);
00674       if (str[0])
00675          sprintf(send_message, "[%s] ", str);
00676       else
00677          send_message[0] = 0;
00678    }
00679 
00680    local_message[0] = 0;
00681 
00682    /* preceed error messages with file and line info */
00683    if (message_type == MT_ERROR) {
00684       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
00685       strcat(send_message, str);
00686       strcat(local_message, str);
00687    }
00688 
00689    /* print argument list into message */
00690    va_start(argptr, format);
00691    vsprintf(str, (char *) format, argptr);
00692    va_end(argptr);
00693 
00694    if (facility)
00695       sprintf(local_message + strlen(local_message), "{%s} ", facility);
00696 
00697    strcat(send_message, str);
00698    strcat(local_message, str);
00699 
00700    /* call user function if set via cm_set_msg_print */
00701    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00702       _message_print(local_message);
00703 
00704    /* return if system mask is not set */
00705    if ((message_type & _message_mask_system) == 0) {
00706       in_routine = FALSE;
00707       return CM_SUCCESS;
00708    }
00709 
00710    /* copy message to event */
00711    pevent = (EVENT_HEADER *) event;
00712    strcpy(event + sizeof(EVENT_HEADER), send_message);
00713 
00714    /* send event if not of type MLOG */
00715    if (message_type != MT_LOG) {
00716       /* if no message buffer already opened, do so now */
00717       if (_msg_buffer == 0) {
00718          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00719          if (status != BM_SUCCESS && status != BM_CREATED) {
00720             in_routine = FALSE;
00721             return status;
00722          }
00723       }
00724 
00725       /* setup the event header and send the message */
00726       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00727                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00728       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00729    }
00730 
00731    /* log message */
00732    cm_msg_log1(message_type, send_message, facility);
00733 
00734    in_routine = FALSE;
00735 
00736    return CM_SUCCESS;
00737 }
00738 
00739 /********************************************************************/
00740 /** 
00741 Register a dispatch function for receiving system messages.
00742 - example code from mlxspeaker.c
00743 \code
00744 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
00745 {
00746   char str[256], *pc, *sp;
00747   // print message
00748   printf("%s\n", (char *)(message));
00749 
00750   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
00751                  ,header->event_id
00752                  ,header->trigger_mask
00753                  ,header->serial_number
00754                  ,header->data_size);
00755   pc = strchr((char *)(message),']')+2;
00756   ...
00757   // skip none talking message
00758   if (header->trigger_mask == MT_TALK ||
00759       header->trigger_mask == MT_USER)
00760    ...
00761 }
00762 
00763 int main(int argc, char *argv[])
00764 {
00765   ...
00766   // now connect to server
00767   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
00768   if (status != CM_SUCCESS)
00769     return 1;
00770   // Register callback for messages
00771   cm_msg_register(receive_message);
00772   ...
00773 }
00774 \endcode
00775 @param func Dispatch function.
00776 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
00777 */
00778 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
00779 {
00780    INT status, id;
00781 
00782    /* if no message buffer already opened, do so now */
00783    if (_msg_buffer == 0) {
00784       status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00785       if (status != BM_SUCCESS && status != BM_CREATED)
00786          return status;
00787    }
00788 
00789    _msg_dispatch = func;
00790 
00791    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_SOME, &id, func);
00792 
00793    return status;
00794 }
00795 
00796 /********************************************************************/
00797 /**
00798 Retrieve old messages from log file 
00799 @param  n_message        Number of messages to retrieve
00800 @param  message          buf_size bytes of messages, separated
00801                          by \n characters. The returned number
00802                          of bytes is normally smaller than the
00803                          initial buf_size, since only full
00804                          lines are returned.
00805 @param *buf_size         Size of message buffer to fill
00806 @return CM_SUCCESS
00807 */
00808 INT cm_msg_retrieve(INT n_message, char *message, INT * buf_size)
00809 {
00810    char dir[256];
00811    char filename[256];
00812    char path[256], *p;
00813    FILE *f;
00814    INT status, size, offset, i;
00815    HNDLE hDB, hKey;
00816 
00817 
00818    if (rpc_is_remote())
00819       return rpc_call(RPC_CM_MSG_RETRIEVE, message, buf_size);
00820 
00821    cm_get_experiment_database(&hDB, NULL);
00822 
00823    if (hDB) {
00824       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00825       if (status == DB_SUCCESS) {
00826          size = sizeof(dir);
00827          memset(dir, 0, size);
00828          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00829          if (dir[0] != 0)
00830             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00831                strcat(dir, DIR_SEPARATOR_STR);
00832 
00833          strcpy(filename, "midas.log");
00834          size = sizeof(filename);
00835          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00836 
00837          strcpy(path, dir);
00838          strcat(path, filename);
00839       } else {
00840          cm_get_path(dir);
00841          if (dir[0] != 0)
00842             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00843                strcat(dir, DIR_SEPARATOR_STR);
00844 
00845          strcpy(path, dir);
00846          strcat(path, "midas.log");
00847       }
00848    } else
00849       strcpy(path, "midas.log");
00850 
00851    f = fopen(path, "rb");
00852    if (f == NULL) {
00853       sprintf(message, "Cannot open message log file %s\n", path);
00854       *buf_size = strlen(message);
00855       return CM_DB_ERROR;
00856    } else {
00857       /* position buf_size bytes before the EOF */
00858       fseek(f, -(*buf_size - 1), SEEK_END);
00859       offset = ftell(f);
00860       if (offset != 0) {
00861          /* go to end of line */
00862          fgets(message, *buf_size - 1, f);
00863          offset = ftell(f) - offset;
00864          *buf_size -= offset;
00865       }
00866 
00867       memset(message, 0, *buf_size);
00868       fread(message, 1, *buf_size - 1, f);
00869       message[*buf_size - 1] = 0;
00870       fclose(f);
00871 
00872       p = message + (*buf_size - 2);
00873 
00874       /* goto end of buffer */
00875       while (p != message && *p == 0)
00876          p--;
00877 
00878       /* strip line break */
00879       while (p != message && (*p == '\n' || *p == '\r'))
00880          *(p--) = 0;
00881 
00882       /* trim buffer so that last n_messages remain */
00883       for (i = 0; i < n_message; i++) {
00884          while (p != message && *p != '\n')
00885             p--;
00886 
00887          while (p != message && (*p == '\n' || *p == '\r'))
00888             p--;
00889       }
00890       if (p != message) {
00891          p++;
00892          while (*p == '\n' || *p == '\r')
00893             p++;
00894       }
00895 
00896       *buf_size = (*buf_size - 1) - ((PTYPE) p - (PTYPE) message);
00897 
00898       memmove(message, p, *buf_size);
00899       message[*buf_size] = 0;
00900    }
00901 
00902    return CM_SUCCESS;
00903 }
00904 
00905 /**dox***************************************************************/
00906                    /** @} *//* end of msgfunctionc */
00907 
00908 /**dox***************************************************************/
00909 /** @addtogroup cmfunctionc
00910  *  
00911  *  @{  */
00912 
00913 /********************************************************************/
00914 /**
00915 Get time from MIDAS server and set local time.
00916 @param    seconds         Time in seconds
00917 @return CM_SUCCESS
00918 */
00919 INT cm_synchronize(DWORD * seconds)
00920 {
00921    INT sec, status;
00922 
00923    /* if connected to server, get time from there */
00924    if (rpc_is_remote()) {
00925       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
00926 
00927       /* set local time */
00928       if (status == CM_SUCCESS)
00929          ss_settime(sec);
00930    }
00931 
00932    /* return time to caller */
00933    if (seconds != NULL) {
00934       *seconds = ss_time();
00935    }
00936 
00937    return CM_SUCCESS;
00938 }
00939 
00940 /********************************************************************/
00941 /**
00942 Get time from MIDAS server and set local time.
00943 @param    str            return time string
00944 @param    buf_size       Maximum size of str
00945 @return   CM_SUCCESS
00946 */
00947 INT cm_asctime(char *str, INT buf_size)
00948 {
00949    /* if connected to server, get time from there */
00950    if (rpc_is_remote())
00951       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
00952 
00953    /* return local time */
00954    strcpy(str, ss_asctime());
00955 
00956    return CM_SUCCESS;
00957 }
00958 
00959 /********************************************************************/
00960 /**
00961 Get time from ss_time on server.
00962 @param    time string
00963 @return   CM_SUCCESS
00964 */
00965 INT cm_time(DWORD * time)
00966 {
00967    /* if connected to server, get time from there */
00968    if (rpc_is_remote())
00969       return rpc_call(RPC_CM_TIME, time);
00970 
00971    /* return local time */
00972    *time = ss_time();
00973 
00974    return CM_SUCCESS;
00975 }
00976 
00977 /**dox***************************************************************/
00978                    /** @} *//* end of cmfunctionc */
00979 
00980 /********************************************************************\
00981 *                                                                    *
00982 *           cm_xxx  -  Common Functions to buffer & database         *
00983 *                                                                    *
00984 \********************************************************************/
00985 
00986 /* Globals */
00987 
00988 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
00989 static HNDLE _hDB = 0;          /* Database handle */
00990 static char _client_name[NAME_LENGTH];
00991 static char _path_name[MAX_STRING_LENGTH];
00992 static INT _call_watchdog = TRUE;
00993 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
00994 INT _mutex_alarm, _mutex_elog;
00995 
00996 /**dox***************************************************************/
00997 /** @addtogroup cmfunctionc
00998  *  
00999  *  @{  */
01000 
01001 /**
01002 Return version number of current MIDAS library as a string
01003 @return version number * 100
01004 */
01005 char *cm_get_version()
01006 {
01007    return MIDAS_VERSION;
01008 }
01009 
01010 /********************************************************************/
01011 /**
01012 Set path to actual experiment. This function gets called
01013 by cm_connect_experiment if the connection is established
01014 to a local experiment (not through the TCP/IP server).
01015 The path is then used for all shared memory routines.
01016 @param  path             Pathname
01017 @return CM_SUCCESS
01018 */
01019 INT cm_set_path(char *path)
01020 {
01021    strcpy(_path_name, path);
01022 
01023    /* check for trailing directory seperator */
01024    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01025       strcat(_path_name, DIR_SEPARATOR_STR);
01026 
01027    return CM_SUCCESS;
01028 }
01029 
01030 /********************************************************************/
01031 /**
01032 Return the path name previously set with cm_set_path.
01033 @param  path             Pathname
01034 @return CM_SUCCESS
01035 */
01036 INT cm_get_path(char *path)
01037 {
01038    strcpy(path, _path_name);
01039 
01040    return CM_SUCCESS;
01041 }
01042 
01043 /**dox***************************************************************/
01044                    /** @} *//* end of cmfunctionc */
01045 
01046 /**dox***************************************************************/
01047 /** @addtogroup cmfunctionc
01048  *  
01049  *  @{  */
01050 
01051 /**dox***************************************************************/
01052 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01053 
01054 typedef struct {
01055    char name[NAME_LENGTH];
01056    char directory[MAX_STRING_LENGTH];
01057    char user[NAME_LENGTH];
01058 } experiment_table;
01059 
01060 static experiment_table exptab[MAX_EXPERIMENT];
01061 
01062 /**dox***************************************************************/
01063 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01064 
01065 /**
01066 Scan the "exptab" file for MIDAS experiment names and save them
01067 for later use by rpc_server_accept(). The file is first searched
01068 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01069 @return CM_SUCCESS<br>
01070         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01071 */
01072 INT cm_scan_experiments(void)
01073 {
01074    INT i;
01075    FILE *f;
01076    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01077 
01078    for (i = 0; i < MAX_EXPERIMENT; i++)
01079       exptab[i].name[0] = 0;
01080 
01081    /* MIDAS_DIR overrides exptab */
01082    if (getenv("MIDAS_DIR")) {
01083       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01084 
01085       strcpy(exptab[0].name, "Default");
01086       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01087       exptab[0].user[0] = 0;
01088 
01089       return CM_SUCCESS;
01090    }
01091 
01092    /* default directory for different OSes */
01093 #if defined (OS_WINNT)
01094    if (getenv("SystemRoot"))
01095       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01096    else if (getenv("windir"))
01097       strlcpy(str, getenv("windir"), sizeof(str));
01098    else
01099       strcpy(str, "");
01100 
01101    strcpy(alt_str, str);
01102    strcat(str, "\\system32\\exptab");
01103    strcat(alt_str, "\\system\\exptab");
01104 #elif defined (OS_UNIX)
01105    strcpy(str, "/etc/exptab");
01106    strcpy(alt_str, "/exptab");
01107 #else
01108    strcpy(str, "exptab");
01109    strcpy(alt_str, "exptab");
01110 #endif
01111 
01112    /* MIDAS_EXPTAB overrides default directory */
01113    if (getenv("MIDAS_EXPTAB")) {
01114       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01115       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01116    }
01117 
01118    /* read list of available experiments */
01119    f = fopen(str, "r");
01120    if (f == NULL) {
01121       f = fopen(alt_str, "r");
01122       if (f == NULL)
01123          return CM_UNDEF_ENVIRON;
01124    }
01125 
01126    i = 0;
01127    if (f != NULL) {
01128       do {
01129          str[0] = 0;
01130          if (fgets(str, 100, f) == NULL)
01131             break;
01132          if (str[0] && str[0] != '#') {
01133             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01134 
01135             /* check for trailing directory separator */
01136             pdir = exptab[i].directory;
01137             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01138                strcat(pdir, DIR_SEPARATOR_STR);
01139 
01140             i++;
01141          }
01142       } while (!feof(f));
01143       fclose(f);
01144    }
01145 
01146    /*
01147       for (j=0 ; j<i ; j++)
01148       {
01149       sprintf(str, "Scanned experiment %s", exptab[j].name);
01150       cm_msg(MINFO, str);
01151       }
01152     */
01153 
01154    return CM_SUCCESS;
01155 }
01156 
01157 /********************************************************************/
01158 /**
01159 Delete client info from database
01160 @param hDB               Database handle
01161 @param pid               PID of entry to delete, zero for this process.
01162 @return CM_SUCCESS
01163 */
01164 INT cm_delete_client_info(HNDLE hDB, INT pid)
01165 {
01166 #ifdef LOCAL_ROUTINES
01167 
01168    /* only do it if local */
01169    if (!rpc_is_remote()) {
01170       INT status;
01171       HNDLE hKey;
01172       char str[256];
01173 
01174       if (!pid)
01175          pid = ss_gettid();
01176 
01177       /* don't delete info from a closed database */
01178       if (_database_entries == 0)
01179          return CM_SUCCESS;
01180 
01181       /* make operation atomic by locking database */
01182       db_lock_database(hDB);
01183 
01184       sprintf(str, "System/Clients/%0d", pid);
01185       status = db_find_key1(hDB, 0, str, &hKey);
01186       if (status != DB_SUCCESS) {
01187          db_unlock_database(hDB);
01188          return status;
01189       }
01190 
01191       /* unlock client entry and delete it without locking DB */
01192       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01193       db_delete_key1(hDB, hKey, 1, TRUE);
01194 
01195       db_unlock_database(hDB);
01196 
01197       /* touch notify key to inform others */
01198       status = 0;
01199       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01200    }
01201 #endif                          /*LOCAL_ROUTINES */
01202 
01203    return CM_SUCCESS;
01204 }
01205 
01206 /********************************************************************/
01207 /**
01208 Check if a client with a /system/client/xxx entry has
01209 a valid entry in the ODB client table. If not, remove
01210 that client from the /system/client tree. 
01211 @param   hDB               Handle to online database
01212 @param   hKeyClient        Handle to client key
01213 @return  CM_SUCCESS, CM_NO_CLIENT
01214 */
01215 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01216 {
01217 #ifdef LOCAL_ROUTINES
01218 
01219    KEY key;
01220    DATABASE_HEADER *pheader;
01221    DATABASE_CLIENT *pclient;
01222    INT i, client_pid, status;
01223    char name[NAME_LENGTH];
01224 
01225    db_get_key(hDB, hKeyClient, &key);
01226    client_pid = atoi(key.name);
01227 
01228    i = sizeof(name);
01229    db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01230 
01231    db_lock_database(hDB);
01232    if (_database[hDB - 1].attached) {
01233       pheader = _database[hDB - 1].database_header;
01234       pclient = pheader->client;
01235 
01236       /* loop through clients */
01237       for (i = 0; i < pheader->max_client_index; i++, pclient++)
01238          if (pclient->tid == client_pid)
01239             break;
01240 
01241       if (i == pheader->max_client_index) {
01242          /* client not found : delete ODB stucture */
01243          db_unlock_database(hDB);
01244 
01245          status = cm_delete_client_info(hDB, client_pid);
01246          if (status != CM_SUCCESS)
01247             cm_msg(MERROR, "cm_check_client", "cannot delete client info");
01248          else
01249             cm_msg(MINFO, "cm_check_clinet",
01250                    "Deleted /System/Clients/%d entry for client %s\n", client_pid, name);
01251 
01252          return CM_NO_CLIENT;
01253       }
01254    }
01255 
01256    db_unlock_database(hDB);
01257 
01258 #endif                          /*LOCAL_ROUTINES */
01259 
01260    return CM_SUCCESS;
01261 }
01262 
01263 /********************************************************************/
01264 /**
01265 Set client information in online database and return handle 
01266 @param  hDB              Handle to online database  
01267 @param  hKeyClient       returned key
01268 @param  host_name        server name 
01269 @param  client_name      Name of this program as it will be seen
01270                          by other clients.
01271 @param  hw_type          Type of byte order
01272 @param  password         MIDAS password  
01273 @param  watchdog_timeout Default watchdog timeout, can be overwritten
01274                          by ODB setting /programs/<name>/Watchdog timeout
01275 @return   CM_SUCCESS
01276 */
01277 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
01278                        char *client_name, INT hw_type, char *password,
01279                        DWORD watchdog_timeout)
01280 {
01281    if (rpc_is_remote())
01282       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
01283                       host_name, client_name, hw_type, password, watchdog_timeout);
01284 
01285 #ifdef LOCAL_ROUTINES
01286    {
01287       INT status, pid, data, i, index, size;
01288       HNDLE hKey, hSubkey;
01289       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
01290       BOOL call_watchdog, allow;
01291       PROGRAM_INFO_STR(program_info_str);
01292 
01293       /* check security if password is presend */
01294       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
01295       if (hKey) {
01296          /* get password */
01297          size = sizeof(pwd);
01298          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
01299 
01300          /* first check allowed hosts list */
01301          allow = FALSE;
01302          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
01303          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
01304             allow = TRUE;
01305 
01306          /* check allowed programs list */
01307          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
01308          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
01309             allow = TRUE;
01310 
01311          /* now check password */
01312          if (!allow &&
01313              strcmp(password, pwd) != 0 && strcmp(password, "mid7qBxsNMHux") != 0) {
01314             if (password[0])
01315                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s",
01316                       host_name);
01317             db_close_all_databases();
01318             bm_close_all_buffers();
01319             _msg_buffer = 0;
01320             return CM_WRONG_PASSWORD;
01321          }
01322       }
01323 
01324       /* make following operation atomic by locking database */
01325       db_lock_database(hDB);
01326 
01327       /* check if entry with this pid exists already */
01328       pid = ss_gettid();
01329 
01330       sprintf(str, "System/Clients/%0d", pid);
01331       status = db_find_key(hDB, 0, str, &hKey);
01332       if (status == DB_SUCCESS) {
01333          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
01334          db_delete_key(hDB, hKey, TRUE);
01335       }
01336 
01337       if (strlen(client_name) >= NAME_LENGTH)
01338          client_name[NAME_LENGTH] = 0;
01339 
01340       strcpy(name, client_name);
01341       strcpy(orig_name, client_name);
01342 
01343       /* check if client name already exists */
01344       status = db_find_key(hDB, 0, "System/Clients", &hKey);
01345 
01346       for (index = 1; status != DB_NO_MORE_SUBKEYS; index++) {
01347          for (i = 0;; i++) {
01348             status = db_enum_key(hDB, hKey, i, &hSubkey);
01349             if (status == DB_NO_MORE_SUBKEYS)
01350                break;
01351 
01352             if (status == DB_SUCCESS) {
01353                size = sizeof(str);
01354                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
01355             }
01356 
01357             /* check if client is living */
01358             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
01359                continue;
01360 
01361             if (equal_ustring(str, name)) {
01362                sprintf(name, "%s%d", client_name, index);
01363                break;
01364             }
01365          }
01366       }
01367 
01368       /* set name */
01369       sprintf(str, "System/Clients/%0d/Name", pid);
01370       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
01371       if (status != DB_SUCCESS) {
01372          db_unlock_database(hDB);
01373          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
01374          return status;
01375       }
01376 
01377       /* copy new client name */
01378       strcpy(client_name, name);
01379       db_set_client_name(hDB, client_name);
01380 
01381       /* set also as rpc name */
01382       rpc_set_name(client_name);
01383 
01384       /* use /system/clients/PID as root */
01385       sprintf(str, "System/Clients/%0d", pid);
01386       db_find_key(hDB, 0, str, &hKey);
01387 
01388       /* set host name */
01389       status =
01390           db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
01391       if (status != DB_SUCCESS) {
01392          db_unlock_database(hDB);
01393          return status;
01394       }
01395 
01396       /* set computer id */
01397       status = db_set_value(hDB, hKey, "Hardware type", &hw_type,
01398                             sizeof(hw_type), 1, TID_INT);
01399       if (status != DB_SUCCESS) {
01400          db_unlock_database(hDB);
01401          return status;
01402       }
01403 
01404       /* set server port */
01405       data = 0;
01406       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
01407       if (status != DB_SUCCESS) {
01408          db_unlock_database(hDB);
01409          return status;
01410       }
01411 
01412       /* lock client entry */
01413       db_set_mode(hDB, hKey, MODE_READ, TRUE);
01414 
01415       /* get (set) default watchdog timeout */
01416       size = sizeof(watchdog_timeout);
01417       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
01418       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01419 
01420       /* define /programs entry */
01421       sprintf(str, "/Programs/%s", orig_name);
01422       db_create_record(hDB, 0, str, strcomb(program_info_str));
01423 
01424       /* save handle for ODB and client */
01425       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
01426       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
01427 
01428       /* save watchdog timeout */
01429       cm_get_watchdog_params(&call_watchdog, NULL);
01430       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01431       if (call_watchdog)
01432          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
01433 
01434       /* end of atomic operations */
01435       db_unlock_database(hDB);
01436 
01437       /* touch notify key to inform others */
01438       data = 0;
01439       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
01440 
01441       *hKeyClient = hKey;
01442    }
01443 #endif                          /* LOCAL_ROUTINES */
01444 
01445    return CM_SUCCESS;
01446 }
01447 
01448 /********************************************************************/
01449 /**
01450 Get info about the current client 
01451 @param  *client_name       Client name.  
01452 @return   CM_SUCCESS, CM_UNDEF_EXP  
01453 */
01454 INT cm_get_client_info(char *client_name)
01455 {
01456    INT status, length;
01457    HNDLE hDB, hKey;
01458 
01459    /* get root key of client */
01460    cm_get_experiment_database(&hDB, &hKey);
01461    if (!hDB) {
01462       client_name[0] = 0;
01463       return CM_UNDEF_EXP;
01464    }
01465 
01466    status = db_find_key(hDB, hKey, "Name", &hKey);
01467    if (status != DB_SUCCESS)
01468       return status;
01469 
01470    length = NAME_LENGTH;
01471    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
01472    if (status != DB_SUCCESS)
01473       return status;
01474 
01475    return CM_SUCCESS;
01476 }
01477 
01478 /********************************************************************/
01479 /**
01480 Returns MIDAS environment variables. 
01481 @attention This function can be used to evaluate the standard MIDAS
01482            environment variables before connecting to an experiment
01483            (see @ref Environment_variables).
01484            The usual way is that the host name and experiment name are first derived
01485            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
01486            They can then be superseded by command line parameters with -h and -e flags.
01487 \code
01488 #include <stdio.h>
01489 #include <midas.h>
01490 main(int argc, char *argv[])
01491 {
01492   INT  status, i;
01493   char host_name[256],exp_name[32];
01494 
01495   // get default values from environment
01496   cm_get_environment(host_name, exp_name);
01497 
01498   // parse command line parameters
01499   for (i=1 ; i<argc ; i++)
01500     {
01501     if (argv[i][0] == '-')
01502       {
01503       if (i+1 >= argc || argv[i+1][0] == '-')
01504         goto usage;
01505       if (argv[i][1] == 'e')
01506         strcpy(exp_name, argv[++i]);
01507       else if (argv[i][1] == 'h')
01508         strcpy(host_name, argv[++i]);
01509       else
01510         {
01511 usage:
01512         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01513         return 1;
01514         }
01515       }
01516     }
01517   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01518   if (status != CM_SUCCESS)
01519     return 1;
01520     ...do anyting...
01521   cm_disconnect_experiment();
01522 }
01523 \endcode
01524 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
01525 @param host_name_size     string length
01526 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
01527 @param exp_name_size      string length
01528 @return CM_SUCCESS
01529 */
01530 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name,
01531                        int exp_name_size)
01532 {
01533    host_name[0] = exp_name[0] = 0;
01534 
01535    if (getenv("MIDAS_SERVER_HOST"))
01536       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
01537 
01538    if (getenv("MIDAS_EXPT_NAME"))
01539       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
01540 
01541    return CM_SUCCESS;
01542 }
01543 
01544 
01545 /**dox***************************************************************/
01546 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01547 
01548 /********************************************************************/
01549 void cm_check_connect(void)
01550 {
01551    if (_hKeyClient)
01552       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
01553 }
01554 
01555 /**dox***************************************************************/
01556 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01557 
01558 /********************************************************************/
01559 /**
01560 This function connects to an existing MIDAS experiment.
01561 This must be the first call in a MIDAS application.
01562 It opens three TCP connection to the remote host (one for RPC calls,
01563 one to send events and one for hot-link notifications from the remote host)
01564 and writes client information into the ODB under /System/Clients.
01565 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
01566 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
01567 experiment name (see @ref Environment_variables).
01568 For that purpose, the function cm_get_environment()
01569 should be called prior to cm_connect_experiment(). If command line
01570 parameters -h and -e are used, the evaluation should be done between
01571 cm_get_environment() and cm_connect_experiment(). The function
01572 cm_disconnect_experiment() must be called before a MIDAS application exits.
01573 \code
01574 #include <stdio.h>
01575 #include <midas.h>
01576 main(int argc, char *argv[])
01577 {
01578   INT  status, i;
01579   char host_name[256],exp_name[32];
01580 
01581   // get default values from environment
01582   cm_get_environment(host_name, exp_name);
01583 
01584   // parse command line parameters
01585   for (i=1 ; i<argc ; i++)
01586     {
01587     if (argv[i][0] == '-')
01588       {
01589       if (i+1 >= argc || argv[i+1][0] == '-')
01590         goto usage;
01591       if (argv[i][1] == 'e')
01592         strcpy(exp_name, argv[++i]);
01593       else if (argv[i][1] == 'h')
01594         strcpy(host_name, argv[++i]);
01595       else
01596         {
01597 usage:
01598         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01599         return 1;
01600         }
01601       }
01602     }
01603   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01604   if (status != CM_SUCCESS)
01605     return 1;
01606   ...do operations...
01607   cm_disconnect_experiment();
01608 }
01609 \endcode
01610 @param host_name Specifies host to connect to. Must be a valid IP host name.
01611   The string can be empty ("") if to connect to the local computer.
01612 @param exp_name Specifies the experiment to connect to.
01613   If this string is empty, the number of defined experiments in exptab is checked.
01614   If only one experiment is defined, the function automatically connects to this
01615   one. If more than one experiment is defined, a list is presented and the user
01616   can interactively select one experiment.
01617 @param client_name Client name of the calling program as it can be seen by
01618   others (like the scl command in ODBEdit).
01619 @param func Callback function to read in a password if security has
01620   been enabled. In all command line applications this function is NULL which
01621   invokes an internal ss_gets() function to read in a password.
01622   In windows environments (MS Windows, X Windows) a function can be supplied to
01623   open a dialog box and read in the password. The argument of this function must
01624   be the returned password.
01625 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br> 
01626 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
01627 */
01628 INT cm_connect_experiment(char *host_name, char *exp_name,
01629                           char *client_name, void (*func) (char *))
01630 {
01631    INT status;
01632    char str[256];
01633 
01634    status = cm_connect_experiment1(host_name, exp_name, client_name,
01635                                    func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
01636    if (status != CM_SUCCESS) {
01637       cm_get_error(status, str);
01638       puts(str);
01639    }
01640 
01641    return status;
01642 }
01643 
01644 /********************************************************************/
01645 /**
01646 Connect to a MIDAS experiment (to the online database) on
01647            a specific host.
01648 @internal
01649 */
01650 INT cm_connect_experiment1(char *host_name, char *exp_name,
01651                            char *client_name, void (*func) (char *),
01652                            INT odb_size, DWORD watchdog_timeout)
01653 {
01654    INT status, i, mutex_elog, mutex_alarm, size;
01655    char local_host_name[HOST_NAME_LENGTH];
01656    char client_name1[NAME_LENGTH];
01657    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
01658    HNDLE hDB, hKeyClient;
01659    BOOL call_watchdog;
01660    RUNINFO_STR(runinfo_str);
01661 
01662    if (_hKeyClient)
01663       cm_disconnect_experiment();
01664 
01665    rpc_set_name(client_name);
01666 
01667    /* check for local host */
01668    if (equal_ustring(host_name, "local"))
01669       host_name[0] = 0;
01670 
01671 #ifdef OS_WINNT
01672    {
01673       WSADATA WSAData;
01674 
01675       /* Start windows sockets */
01676       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01677          return RPC_NET_ERROR;
01678    }
01679 #endif
01680 
01681    /* search for experiment name in exptab */
01682    if (exp_name == NULL)
01683       exp_name = "";
01684 
01685    strcpy(exp_name1, exp_name);
01686    if (exp_name1[0] == 0) {
01687       status = cm_select_experiment(host_name, exp_name1);
01688       if (status != CM_SUCCESS)
01689          return status;
01690    }
01691 
01692    /* connect to MIDAS server */
01693    if (host_name[0]) {
01694       status = rpc_server_connect(host_name, exp_name1);
01695       if (status != RPC_SUCCESS)
01696          return status;
01697 
01698       /* register MIDAS library functions */
01699       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
01700       if (status != RPC_SUCCESS)
01701          return status;
01702    } else {
01703       /* lookup path for *SHM files and save it */
01704       status = cm_scan_experiments();
01705       if (status != CM_SUCCESS)
01706          return status;
01707 
01708       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
01709          if (equal_ustring(exp_name1, exptab[i].name))
01710             break;
01711 
01712       /* return if experiment not defined */
01713       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
01714          /* message should be displayed by application
01715             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
01716             cm_msg(MERROR, str);
01717           */
01718          return CM_UNDEF_EXP;
01719       }
01720 
01721       cm_set_path(exptab[i].directory);
01722 
01723       /* create alarm and elog mutexes */
01724       status = ss_mutex_create("ALARM", &mutex_alarm);
01725       if (status != SS_CREATED && status != SS_SUCCESS) {
01726          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
01727          return status;
01728       }
01729       status = ss_mutex_create("ELOG", &mutex_elog);
01730       if (status != SS_CREATED && status != SS_SUCCESS) {
01731          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
01732          return status;
01733       }
01734       cm_set_experiment_mutex(mutex_alarm, mutex_elog);
01735    }
01736 
01737    /* open ODB */
01738    if (odb_size == 0)
01739       odb_size = DEFAULT_ODB_SIZE;
01740 
01741    status = db_open_database("ODB", odb_size, &hDB, client_name);
01742    if (status != DB_SUCCESS && status != DB_CREATED) {
01743       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01744       return status;
01745    }
01746 
01747    /* now setup client info */
01748    gethostname(local_host_name, sizeof(local_host_name));
01749 
01750    /* check watchdog timeout */
01751    if (watchdog_timeout == 0)
01752       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01753 
01754    strcpy(client_name1, client_name);
01755    password[0] = 0;
01756    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01757                                client_name1, rpc_get_option(0, RPC_OHW_TYPE),
01758                                password, watchdog_timeout);
01759 
01760    if (status == CM_WRONG_PASSWORD) {
01761       if (func == NULL)
01762          strcpy(str, ss_getpass("Password: "));
01763       else
01764          func(str);
01765 
01766       /* re-open database */
01767       status = db_open_database("ODB", odb_size, &hDB, client_name);
01768       if (status != DB_SUCCESS && status != DB_CREATED) {
01769          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01770          return status;
01771       }
01772 
01773       strcpy(password, ss_crypt(str, "mi"));
01774       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01775                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE),
01776                                   password, watchdog_timeout);
01777       if (status != CM_SUCCESS) {
01778          /* disconnect */
01779          if (rpc_is_remote())
01780             rpc_server_disconnect();
01781 
01782          return status;
01783       }
01784    }
01785 
01786    cm_set_experiment_database(hDB, hKeyClient);
01787 
01788    /* set experiment name in ODB */
01789    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
01790 
01791    /* set data dir in ODB */
01792    cm_get_path(str);
01793    size = sizeof(str);
01794    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
01795 
01796    /* check /runinfo structure */
01797    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), FALSE);
01798    if (status == DB_STRUCT_MISMATCH) {
01799       cm_msg(MERROR, "cm_connect_experiment1",
01800              "Aborting on mismatching /Runinfo structure");
01801       cm_disconnect_experiment();
01802       abort();
01803    }
01804 
01805    /* register server to be able to be called by other clients */
01806    status = cm_register_server();
01807    if (status != CM_SUCCESS)
01808       return status;
01809 
01810    /* set watchdog timeout */
01811    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
01812    size = sizeof(watchdog_timeout);
01813    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
01814    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01815    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01816 
01817    /* send startup notification */
01818    if (strchr(local_host_name, '.'))
01819       *strchr(local_host_name, '.') = 0;
01820 
01821    /* startup message is not displayed */
01822    _message_print = NULL;
01823 
01824    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started",
01825           client_name, local_host_name);
01826 
01827    /* enable system and user messages to stdout as default */
01828    cm_set_msg_print(MT_ALL, MT_ALL, puts);
01829 
01830    /* call cm_check_connect when exiting */
01831    atexit((void (*)(void)) cm_check_connect);
01832 
01833    /* register ctrl-c handler */
01834    ss_ctrlc_handler(cm_ctrlc_handler);
01835 
01836    return CM_SUCCESS;
01837 }
01838 
01839 /********************************************************************/
01840 /** 
01841 Connect to a MIDAS server and return all defined
01842            experiments in *exp_name[MAX_EXPERIMENTS]
01843 @param  host_name         Internet host name.
01844 @param  exp_name          list of experiment names
01845 @return CM_SUCCESS, RPC_NET_ERROR
01846 */
01847 INT cm_list_experiments(char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
01848 {
01849    INT i, status;
01850    struct sockaddr_in bind_addr;
01851    INT sock;
01852    char str[MAX_EXPERIMENT * NAME_LENGTH];
01853    struct hostent *phe;
01854 
01855    if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
01856       status = cm_scan_experiments();
01857       if (status != CM_SUCCESS)
01858          return status;
01859 
01860       for (i = 0; i < MAX_EXPERIMENT; i++)
01861          strcpy(exp_name[i], exptab[i].name);
01862 
01863       return CM_SUCCESS;
01864    }
01865 #ifdef OS_WINNT
01866    {
01867       WSADATA WSAData;
01868 
01869       /* Start windows sockets */
01870       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01871          return RPC_NET_ERROR;
01872    }
01873 #endif
01874 
01875    /* create a new socket for connecting to remote server */
01876    sock = socket(AF_INET, SOCK_STREAM, 0);
01877    if (sock == -1) {
01878       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
01879       return RPC_NET_ERROR;
01880    }
01881 
01882    /* connect to remote node */
01883    memset(&bind_addr, 0, sizeof(bind_addr));
01884    bind_addr.sin_family = AF_INET;
01885    bind_addr.sin_addr.s_addr = 0;
01886    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
01887 
01888 #ifdef OS_VXWORKS
01889    {
01890       INT host_addr;
01891 
01892       host_addr = hostGetByName(host_name);
01893       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
01894    }
01895 #else
01896    phe = gethostbyname(host_name);
01897    if (phe == NULL) {
01898       cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
01899       return RPC_NET_ERROR;
01900    }
01901    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
01902 #endif
01903 
01904 #ifdef OS_UNIX
01905    do {
01906       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
01907 
01908       /* don't return if an alarm signal was cought */
01909    } while (status == -1 && errno == EINTR);
01910 #else
01911    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
01912 #endif
01913 
01914    if (status != 0) {
01915 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
01916       return RPC_NET_ERROR;
01917    }
01918 
01919    /* request experiment list */
01920    send(sock, "I", 2, 0);
01921 
01922    for (i = 0; i < MAX_EXPERIMENT; i++) {
01923       exp_name[i][0] = 0;
01924       status = recv_string(sock, str, sizeof(str), 1000);
01925 
01926       if (status < 0)
01927          return RPC_NET_ERROR;
01928 
01929       if (status == 0)
01930          break;
01931 
01932       strcpy(exp_name[i], str);
01933    }
01934 
01935    exp_name[i][0] = 0;
01936    closesocket(sock);
01937 
01938    return CM_SUCCESS;
01939 }
01940 
01941 /********************************************************************/
01942 /**
01943 Connect to a MIDAS server and select an experiment
01944            from the experiments available on this server
01945 @internal
01946 @param  host_name         Internet host name.
01947 @param  exp_name          list of experiment names
01948 @return CM_SUCCESS, RPC_NET_ERROR
01949 */
01950 INT cm_select_experiment(char *host_name, char *exp_name)
01951 {
01952    INT status, i;
01953    char expts[MAX_EXPERIMENT][NAME_LENGTH];
01954    char str[32];
01955 
01956    /* retrieve list of experiments and make selection */
01957    status = cm_list_experiments(host_name, expts);
01958    if (status != CM_SUCCESS)
01959       return status;
01960 
01961    if (expts[1][0]) {
01962       if (host_name[0])
01963          printf("Available experiments on server %s:\n", host_name);
01964       else
01965          printf("Available experiments on local computer:\n");
01966 
01967       for (i = 0; expts[i][0]; i++)
01968          printf("%d : %s\n", i, expts[i]);
01969       printf("Select number: ");
01970       ss_gets(str, 32);
01971       i = atoi(str);
01972       strcpy(exp_name, expts[i]);
01973    } else
01974       strcpy(exp_name, expts[0]);
01975 
01976    return CM_SUCCESS;
01977 }
01978 
01979 /********************************************************************/
01980 /**
01981 Connect to a MIDAS client of the current experiment
01982 @internal
01983 @param  client_name       Name of client to connect to. This name
01984                             is set by the other client via the
01985                             cm_connect_experiment call.
01986 @param  hConn            Connection handle
01987 @return CM_SUCCESS, CM_NO_CLIENT
01988 */
01989 INT cm_connect_client(char *client_name, HNDLE * hConn)
01990 {
01991    HNDLE hDB, hKeyRoot, hSubkey, hKey;
01992    INT status, i, length, port;
01993    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
01994 
01995    /* find client entry in ODB */
01996    cm_get_experiment_database(&hDB, &hKey);
01997 
01998    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
01999    if (status != DB_SUCCESS)
02000       return status;
02001 
02002    i = 0;
02003    do {
02004       /* search for client with specific name */
02005       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02006       if (status == DB_NO_MORE_SUBKEYS)
02007          return CM_NO_CLIENT;
02008 
02009       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02010       if (status != DB_SUCCESS)
02011          return status;
02012 
02013       length = NAME_LENGTH;
02014       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02015       if (status != DB_SUCCESS)
02016          return status;
02017 
02018       if (equal_ustring(name, client_name)) {
02019          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02020          if (status != DB_SUCCESS)
02021             return status;
02022 
02023          length = sizeof(INT);
02024          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02025          if (status != DB_SUCCESS)
02026             return status;
02027 
02028          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02029          if (status != DB_SUCCESS)
02030             return status;
02031 
02032          length = sizeof(host_name);
02033          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02034          if (status != DB_SUCCESS)
02035             return status;
02036 
02037          /* client found -> connect to its server port */
02038          return rpc_client_connect(host_name, port, client_name, hConn);
02039       }
02040 
02041 
02042    } while (TRUE);
02043 }
02044 
02045 /********************************************************************/
02046 /**
02047 Disconnect from a MIDAS client 
02048 @param   hConn             Connection handle obtained via
02049                              cm_connect_client()
02050 @param   bShutdown         If TRUE, disconnect from client and
02051                              shut it down (exit the client program)
02052                              by sending a RPC_SHUTDOWN message
02053 @return   see rpc_client_disconnect()
02054 */
02055 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02056 {
02057    return rpc_client_disconnect(hConn, bShutdown);
02058 }
02059 
02060 /********************************************************************/
02061 /**
02062 Disconnect from a MIDAS experiment.
02063 @attention Should be the last call to a MIDAS library function in an
02064 application before it exits. This function removes the client information
02065 from the ODB, disconnects all TCP connections and frees all internal
02066 allocated memory. See cm_connect_experiment() for example.
02067 @return CM_SUCCESS
02068 */
02069 INT cm_disconnect_experiment(void)
02070 {
02071    HNDLE hDB, hKey;
02072    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02073 
02074    /* send shutdown notification */
02075    rpc_get_name(client_name);
02076    gethostname(local_host_name, sizeof(local_host_name));
02077    if (strchr(local_host_name, '.'))
02078       *strchr(local_host_name, '.') = 0;
02079 
02080    /* disconnect message not displayed */
02081    _message_print = NULL;
02082 
02083    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped",
02084           client_name, local_host_name);
02085 
02086    if (rpc_is_remote()) {
02087       /* close open records */
02088       db_close_all_records();
02089 
02090       rpc_client_disconnect(-1, FALSE);
02091       rpc_server_disconnect();
02092    } else {
02093       rpc_client_disconnect(-1, FALSE);
02094 
02095 #ifdef LOCAL_ROUTINES
02096       ss_alarm(0, cm_watchdog);
02097       _watchdog_last_called = 0;
02098 #endif                          /* LOCAL_ROUTINES */
02099 
02100       /* delete client info */
02101       cm_get_experiment_database(&hDB, &hKey);
02102 
02103       if (hDB)
02104          cm_delete_client_info(hDB, 0);
02105 
02106       bm_close_all_buffers();
02107       db_close_all_databases();
02108    }
02109 
02110    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02111       rpc_server_shutdown();
02112 
02113    /* free RPC list */
02114    rpc_deregister_functions();
02115 
02116    cm_set_experiment_database(0, 0);
02117 
02118    _msg_buffer = 0;
02119 
02120    /* free memory buffers */
02121    if (_event_buffer_size > 0) {
02122       M_FREE(_event_buffer);
02123       _event_buffer_size = 0;
02124    }
02125 
02126    if (_net_recv_buffer_size > 0) {
02127       M_FREE(_net_recv_buffer);
02128       _net_recv_buffer_size = 0;
02129    }
02130 
02131    if (_net_send_buffer_size > 0) {
02132       M_FREE(_net_send_buffer);
02133       _net_send_buffer_size = 0;
02134    }
02135 
02136    if (_tcp_buffer != NULL) {
02137       M_FREE(_tcp_buffer);
02138       _tcp_buffer = NULL;
02139    }
02140 
02141    return CM_SUCCESS;
02142 }
02143 
02144 /********************************************************************/
02145 /**
02146 Set the handle to the ODB for the currently connected experiment
02147 @param hDB              Database handle
02148 @param hKeyClient       Key handle of client structure
02149 @return CM_SUCCESS
02150 */
02151 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02152 {
02153    _hDB = hDB;
02154    _hKeyClient = hKeyClient;
02155 
02156    return CM_SUCCESS;
02157 }
02158 
02159 
02160 
02161 /**dox***************************************************************/
02162 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02163 
02164 /********************************************************************/
02165 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog)
02166 /********************************************************************\
02167 
02168   Routine: cm_set_experiment_mutex
02169 
02170   Purpose: Set the handle to the experiment wide mutexes
02171 
02172   Input:
02173     INT    mutex_alarm      Alarm mutex
02174     INT    mutex_elog       Elog mutex
02175 
02176   Output:
02177     none
02178 
02179   Function value:
02180     CM_SUCCESS              Successful completion
02181 
02182 \********************************************************************/
02183 {
02184    _mutex_alarm = mutex_alarm;
02185    _mutex_elog = mutex_elog;
02186 
02187    return CM_SUCCESS;
02188 }
02189 
02190 /**dox***************************************************************/
02191 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02192 
02193 /********************************************************************/
02194 /** 
02195 Get the handle to the ODB from the currently connected experiment.
02196 
02197 @attention This function returns the handle of the online database (ODB) which
02198 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02199 to access the client information in the ODB. If the client key handle is not needed,
02200 the parameter can be NULL.
02201 \code
02202 HNDLE hDB, hkeyclient;
02203  char  name[32];
02204  int   size;
02205  db_get_experiment_database(&hdb, &hkeyclient);
02206  size = sizeof(name);
02207  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02208  printf("My name is %s\n", name);
02209 \endcode
02210 @param hDB Database handle.
02211 @param hKeyClient Handle for key where search starts, zero for root.
02212 @return CM_SUCCESS
02213 */
02214 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02215 {
02216    if (_hDB) {
02217       if (hDB != NULL)
02218          *hDB = _hDB;
02219       if (hKeyClient != NULL)
02220          *hKeyClient = _hKeyClient;
02221    } else {
02222       if (hDB != NULL)
02223          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02224       if (hKeyClient != NULL)
02225          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02226    }
02227 
02228    return CM_SUCCESS;
02229 }
02230 
02231 /**dox***************************************************************/
02232 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02233 
02234 /********************************************************************/
02235 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog)
02236 /********************************************************************\
02237 
02238   Routine: cm_get_experiment_mutex
02239 
02240   Purpose: Get the handle to the experiment wide mutexes
02241 
02242   Input:
02243     none
02244 
02245   Output:
02246     INT    mutex_alarm      Alarm mutex
02247     INT    mutex_elog       Elog mutex
02248 
02249   Function value:
02250     CM_SUCCESS              Successful completion
02251 
02252 \********************************************************************/
02253 {
02254    if (mutex_alarm)
02255       *mutex_alarm = _mutex_alarm;
02256    if (mutex_elog)
02257       *mutex_elog = _mutex_elog;
02258 
02259    return CM_SUCCESS;
02260 }
02261 
02262 /**dox***************************************************************/
02263 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02264 
02265 /********************************************************************/
02266 /**
02267 Sets the internal watchdog flags and the own timeout.
02268 If call_watchdog is TRUE, the cm_watchdog routine is called
02269 periodically from the system to show other clients that
02270 this application is "alive". On UNIX systems, the
02271 alarm() timer is used which is then not available for
02272 user purposes.
02273 
02274 The timeout specifies the time, after which the calling
02275 application should be considered "dead" by other clients.
02276 Normally, the cm_watchdog() routines is called periodically.
02277 If a client crashes, this does not occur any more. Then
02278 other clients can detect this and clear all buffer and
02279 database entries of this application so they are not
02280 blocked any more. If this application should not checked
02281 by others, the timeout can be specified as zero.
02282 It might be useful for debugging purposes to do so,
02283 because if a debugger comes to a breakpoint and stops
02284 the application, the periodic call of cm_watchdog
02285 is disabled and the client looks like dead.
02286 
02287 If the timeout is not zero, but the watchdog is not
02288 called (call_watchdog == FALSE), the user must ensure
02289 to call cm_watchdog periodically with a period of
02290 WATCHDOG_INTERVAL milliseconds or less.
02291 
02292 An application which calles system routines which block
02293 the alarm signal for some time, might increase the
02294 timeout to the maximum expected blocking time before
02295 issuing the calls. One example is the logger doing
02296 Exabyte tape IO, which can take up to one minute.
02297 @param    call_watchdog   Call the cm_watchdog routine periodically
02298 @param    timeout         Timeout for this application in ms
02299 @return   CM_SUCCESS
02300 */
02301 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
02302 {
02303    INT i;
02304 
02305    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
02306    _watchdog_timeout = timeout;
02307 
02308    if (rpc_is_remote())
02309       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
02310 
02311 #ifdef LOCAL_ROUTINES
02312 
02313    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
02314       HNDLE hDB, hKey;
02315 
02316       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
02317 
02318       /* write timeout value to client enty in ODB */
02319       cm_get_experiment_database(&hDB, &hKey);
02320 
02321       if (hDB) {
02322          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02323          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
02324          db_set_mode(hDB, hKey, MODE_READ, TRUE);
02325       }
02326    } else {
02327       _call_watchdog = call_watchdog;
02328       _watchdog_timeout = timeout;
02329 
02330       /* set watchdog flag of all open buffers */
02331       for (i = _buffer_entries; i > 0; i--) {
02332          BUFFER_CLIENT *pclient;
02333          BUFFER_HEADER *pheader;
02334          INT index;
02335 
02336          index = _buffer[i - 1].client_index;
02337          pheader = _buffer[i - 1].buffer_header;
02338          pclient = &pheader->client[index];
02339 
02340          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02341              _buffer[i - 1].index != rpc_get_server_acception())
02342             continue;
02343 
02344          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
02345              _buffer[i - 1].index != ss_gettid())
02346             continue;
02347 
02348          if (!_buffer[i - 1].attached)
02349             continue;
02350 
02351          /* clear entry from client structure in buffer header */
02352          pclient->watchdog_timeout = timeout;
02353 
02354          /* show activity */
02355          pclient->last_activity = ss_millitime();
02356       }
02357 
02358       /* set watchdog flag of alll open databases */
02359       for (i = _database_entries; i > 0; i--) {
02360          DATABASE_HEADER *pheader;
02361          DATABASE_CLIENT *pclient;
02362          INT index;
02363 
02364          db_lock_database(i);
02365          index = _database[i - 1].client_index;
02366          pheader = _database[i - 1].database_header;
02367          pclient = &pheader->client[index];
02368 
02369          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02370              _database[i - 1].index != rpc_get_server_acception()) {
02371             db_unlock_database(i);
02372             continue;
02373          }
02374 
02375          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
02376              _database[i - 1].index != ss_gettid()) {
02377             db_unlock_database(i);
02378             continue;
02379          }
02380 
02381          if (!_database[i - 1].attached) {
02382             db_unlock_database(i);
02383             continue;
02384          }
02385 
02386          /* clear entry from client structure in buffer header */
02387          pclient->watchdog_timeout = timeout;
02388 
02389          /* show activity */
02390          pclient->last_activity = ss_millitime();
02391 
02392          db_unlock_database(i);
02393       }
02394 
02395       if (call_watchdog)
02396          /* restart watchdog */
02397          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02398       else
02399          /* kill current timer */
02400          ss_alarm(0, cm_watchdog);
02401    }
02402 
02403 #endif                          /* LOCAL_ROUTINES */
02404 
02405    return CM_SUCCESS;
02406 }
02407 
02408 /********************************************************************/
02409 /**
02410 Return the current watchdog parameters
02411 @param call_watchdog   Call the cm_watchdog routine periodically
02412 @param timeout         Timeout for this application in seconds
02413 @return   CM_SUCCESS
02414 */
02415 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
02416 {
02417    if (call_watchdog)
02418       *call_watchdog = _call_watchdog;
02419    if (timeout)
02420       *timeout = _watchdog_timeout;
02421 
02422    return CM_SUCCESS;
02423 }
02424 
02425 /********************************************************************/
02426 /**
02427 Return watchdog information about specific client
02428 @param    hDB              ODB handle
02429 @param    client_name     ODB client name
02430 @param    timeout         Timeout for this application in seconds
02431 @param    last            Last time watchdog was called in msec
02432 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE 
02433 */
02434 
02435 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
02436 {
02437    if (rpc_is_remote())
02438       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
02439 
02440 #ifdef LOCAL_ROUTINES
02441    {
02442       DATABASE_HEADER *pheader;
02443       DATABASE_CLIENT *pclient;
02444       INT i;
02445 
02446       if (hDB > _database_entries || hDB <= 0) {
02447          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02448          return DB_INVALID_HANDLE;
02449       }
02450 
02451       if (!_database[hDB - 1].attached) {
02452          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02453          return DB_INVALID_HANDLE;
02454       }
02455 
02456       /* lock database */
02457       db_lock_database(hDB);
02458 
02459       pheader = _database[hDB - 1].database_header;
02460       pclient = pheader->client;
02461 
02462       /* find client */
02463       for (i = 0; i < pheader->max_client_index; i++, pclient++)
02464          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
02465             *timeout = pclient->watchdog_timeout;
02466             *last = ss_millitime() - pclient->last_activity;
02467             db_unlock_database(hDB);
02468             return CM_SUCCESS;
02469          }
02470 
02471       *timeout = *last = 0;
02472 
02473       db_unlock_database(hDB);
02474 
02475       return CM_NO_CLIENT;
02476    }
02477 #else                           /* LOCAL_ROUTINES */
02478    return CM_SUCCESS;
02479 #endif                          /* LOCAL_ROUTINES */
02480 }
02481 
02482 
02483 /**dox***************************************************************/
02484 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02485 
02486 /********************************************************************/
02487 INT cm_register_server(void)
02488 /********************************************************************\
02489 
02490   Routine: cm_register_server
02491 
02492   Purpose: Register a server which can be called from other clients
02493            of a specific experiment.
02494 
02495   Input:
02496     none
02497 
02498   Output:
02499     none
02500 
02501   Function value:
02502     CM_SUCCESS              Successful completion
02503 
02504 \********************************************************************/
02505 {
02506    INT status, port;
02507    HNDLE hDB, hKey;
02508 
02509    if (!_server_registered) {
02510       port = 0;
02511       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
02512       if (status != RPC_SUCCESS)
02513          return status;
02514       _server_registered = TRUE;
02515 
02516       /* register MIDAS library functions */
02517       rpc_register_functions(rpc_get_internal_list(1), NULL);
02518 
02519       /* store port number in ODB */
02520       cm_get_experiment_database(&hDB, &hKey);
02521 
02522       status = db_find_key(hDB, hKey, "Server Port", &hKey);
02523       if (status != DB_SUCCESS)
02524          return status;
02525 
02526       /* unlock database */
02527       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02528 
02529       /* set value */
02530       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
02531       if (status != DB_SUCCESS)
02532          return status;
02533 
02534       /* lock database */
02535       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02536    }
02537 
02538    return CM_SUCCESS;
02539 }
02540 
02541 /**dox***************************************************************/
02542 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02543 
02544 /********************************************************************/
02545 /**
02546 Registers a callback function for run transitions.
02547 This function internally registers the transition callback
02548 function and publishes its request for transition notification by writing
02549 a transition request to /System/Clients/<pid>/Transition XXX.
02550 Other clients making a transition scan the transition requests of all clients
02551 and call their transition callbacks via RPC.
02552 
02553 Clients can register for transitions (Start/Stop/Pause/Resume) in a given
02554 sequence. All sequence numbers given in the registration are sorted on 
02555 a transition and the clients are contacted in ascending order. By default,
02556 all programs register with a sequence number of 500. The logger however
02557 uses 200 for start, so that it can open files before the other clients
02558 are contacted, and 800 for stop, so that the files get closed when all
02559 other clients have gone already through the stop trantition.
02560 
02561 The callback function returns CM_SUCCESS if it can perform the transition or
02562 a value larger than one in case of error. An error string can be copied
02563 into the error variable.
02564 @attention The callback function will be called on transitions from inside the
02565     cm_yield() function which therefore must be contained in the main program loop.
02566 \code
02567 INT start(INT run_number, char *error)
02568 {
02569   if (<not ok>)
02570     {
02571     strcpy(error, "Cannot start because ...");
02572     return 2;
02573     }
02574   printf("Starting run %d\n", run_number);
02575   return CM_SUCCESS;
02576 }
02577 main()
02578 {
02579   ...
02580   cm_register_transition(TR_START, start, 500);
02581   do
02582     {
02583     status = cm_yield(1000);
02584     } while (status != RPC_SHUTDOWN &&
02585              status != SS_ABORT);
02586   ...
02587 }
02588 \endcode
02589 @param transition Transition to register for (see @ref state_transition)
02590 @param func Callback function.
02591 @param sequence_number Sequence number for that transition (1..1000)
02592 @return CM_SUCCESS
02593 */
02594 INT cm_register_transition(INT transition, INT(*func) (INT, char *), INT sequence_number)
02595 {
02596    INT status, i;
02597    HNDLE hDB, hKey, hKeyTrans;
02598    KEY key;
02599    char str[256];
02600 
02601    /* check for valid transition */
02602    if (transition != TR_START && transition != TR_STOP &&
02603        transition != TR_PAUSE && transition != TR_RESUME) {
02604       cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
02605       return CM_INVALID_TRANSITION;
02606    }
02607 
02608    cm_get_experiment_database(&hDB, &hKey);
02609 
02610    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
02611 
02612    /* find empty slot */
02613    for (i = 0; i < MAX_TRANSITIONS; i++)
02614       if (!_trans_table[i].transition)
02615          break;
02616 
02617    if (i == MAX_TRANSITIONS) {
02618       cm_msg(MERROR, "cm_register_transition",
02619              "To many transition registrations. Please increase MAX_TRANSITIONS and recompile");
02620       return CM_TOO_MANY_REQUESTS;
02621    }
02622 
02623    _trans_table[i].transition = transition;
02624    _trans_table[i].func = func;
02625    _trans_table[i].sequence_number = sequence_number;
02626 
02627    for (i = 0; i < 13; i++)
02628       if (trans_name[i].transition == transition)
02629          break;
02630 
02631    sprintf(str, "Transition %s", trans_name[i].name);
02632 
02633    /* unlock database */
02634    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02635 
02636    /* set value */
02637    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02638    if (!hKeyTrans) {
02639       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02640       if (status != DB_SUCCESS)
02641          return status;
02642    } else {
02643       status = db_get_key(hDB, hKeyTrans, &key);
02644       if (status != DB_SUCCESS)
02645          return status;
02646       status =
02647           db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values,
02648                             TID_INT);
02649       if (status != DB_SUCCESS)
02650          return status;
02651    }
02652 
02653    /* re-lock database */
02654    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02655 
02656    return CM_SUCCESS;
02657 }
02658 
02659 /********************************************************************/
02660 /**
02661 Change the transition sequence for the calling program.
02662 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02663 @param sequence_number New sequence number, should be between 1 and 1000
02664 @return     CM_SUCCESS
02665 */
02666 INT cm_set_transition_sequence(INT transition, INT sequence_number)
02667 {
02668    INT status, i;
02669    HNDLE hDB, hKey;
02670    char str[256];
02671 
02672    /* check for valid transition */
02673    if (transition != TR_START && transition != TR_STOP &&
02674        transition != TR_PAUSE && transition != TR_RESUME) {
02675       cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
02676       return CM_INVALID_TRANSITION;
02677    }
02678 
02679    cm_get_experiment_database(&hDB, &hKey);
02680 
02681    /* Find the transition type from the list */
02682    for (i = 0; i < 13; i++)
02683       if (trans_name[i].transition == transition)
02684          break;
02685    sprintf(str, "Transition %s", trans_name[i].name);
02686 
02687    /* Change local sequence number for this transition type */
02688    for (i = 0; i < MAX_TRANSITIONS; i++)
02689       if (_trans_table[i].transition == transition) {
02690          _trans_table[i].sequence_number = sequence_number;
02691          break;
02692       }
02693 
02694    /* unlock database */
02695    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02696 
02697    /* set value */
02698    status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02699    if (status != DB_SUCCESS)
02700       return status;
02701 
02702    /* re-lock database */
02703    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02704 
02705    return CM_SUCCESS;
02706 
02707 }
02708 
02709 /**dox***************************************************************/
02710 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02711 
02712 static INT _requested_transition;
02713 static DWORD _deferred_transition_mask;
02714 
02715 /**dox***************************************************************/
02716 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02717 
02718 /********************************************************************/
02719 /**
02720 Register a deferred transition handler. If a client is
02721 registered as a deferred transition handler, it may defer
02722 a requested transition by returning FALSE until a certain
02723 condition (like a motor reaches its end position) is
02724 reached.
02725 @param transition      One of TR_xxx
02726 @param (*func)         Function which gets called whenever
02727                        a transition is requested. If it returns
02728                        FALSE, the transition is not performed.
02729 @return CM_SUCCESS,    <error> Error from ODB access
02730 */
02731 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
02732 {
02733    INT status, i, size;
02734    char tr_key_name[256];
02735    HNDLE hDB, hKey;
02736 
02737    cm_get_experiment_database(&hDB, &hKey);
02738 
02739    for (i = 0; _deferred_trans_table[i].transition; i++)
02740       if (_deferred_trans_table[i].transition == transition)
02741          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
02742 
02743    /* set new transition mask */
02744    _deferred_transition_mask |= transition;
02745 
02746    for (i = 0; i < 13; i++)
02747       if (trans_name[i].transition == transition)
02748          break;
02749 
02750    sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
02751 
02752    /* unlock database */
02753    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02754 
02755    /* set value */
02756    i = 0;
02757    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT);
02758    if (status != DB_SUCCESS)
02759       return status;
02760 
02761    /* re-lock database */
02762    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02763 
02764    /* hot link requested transition */
02765    size = sizeof(_requested_transition);
02766    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size,
02767                 TID_INT, TRUE);
02768    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
02769    status =
02770        db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL,
02771                       NULL);
02772    if (status != DB_SUCCESS) {
02773       cm_msg(MERROR, "cm_register_deferred_transition",
02774              "Cannot hotlink /Runinfo/Requested Transition");
02775       return status;
02776    }
02777 
02778    return CM_SUCCESS;
02779 }
02780 
02781 /********************************************************************/
02782 /**
02783 Check for any deferred transition. If a deferred transition
02784 handler has been registered via the
02785 cm_register_deferred_transition function, this routine
02786 should be called regularly. It checks if a transition
02787 request is pending. If so, it calld the registered handler
02788 if the transition should be done and then actually does
02789 the transition.
02790 @return     CM_SUCCESS, <error>  Error from cm_transition()
02791 */
02792 INT cm_check_deferred_transition()
02793 {
02794    INT i, status;
02795    char str[256];
02796    static BOOL first;
02797 
02798    if (_requested_transition == 0)
02799       first = TRUE;
02800 
02801    if (_requested_transition & _deferred_transition_mask) {
02802       for (i = 0; _deferred_trans_table[i].transition; i++)
02803          if (_deferred_trans_table[i].transition == _requested_transition)
02804             break;
02805 
02806       if (_deferred_trans_table[i].transition == _requested_transition) {
02807          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition,
02808                                                                    first)) {
02809             status =
02810                 cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str),
02811                               SYNC, FALSE);
02812             if (status != CM_SUCCESS)
02813                cm_msg(MERROR, "cm_check_deferred_transition",
02814                       "Cannot perform deferred transition: %s", str);
02815 
02816             /* bypass hotlink and set _requested_transition directly to zero */
02817             _requested_transition = 0;
02818 
02819             return status;
02820          }
02821          first = FALSE;
02822       }
02823    }
02824 
02825    return SUCCESS;
02826 }
02827 
02828 
02829 /**dox***************************************************************/
02830 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02831 
02832 /********************************************************************/
02833 
02834 /**dox***************************************************************/
02835 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02836 
02837 typedef struct {
02838    int sequence_number;
02839    char host_name[HOST_NAME_LENGTH];
02840    char client_name[NAME_LENGTH];
02841    int port;
02842 } TR_CLIENT;
02843 
02844 int tr_compare(const void *arg1, const void *arg2)
02845 {
02846    return ((TR_CLIENT *) arg1)->sequence_number - ((TR_CLIENT *) arg2)->sequence_number;
02847 }
02848 
02849 /********************************************************************/
02850 /**
02851 Performs a run transition (Start/Stop/Pause/Resume).
02852 
02853 Synchronous/Asynchronous flag.
02854 If set to ASYNC, the transition is done
02855 asynchronously, meaning that clients are connected and told to execute their
02856 callback routine, but no result is awaited. The return value is
02857 specified by the transition callback function on the remote clients. If all callbacks
02858 can perform the transition, CM_SUCCESS is returned. If one callback cannot
02859 perform the transition, the return value of this callback is returned from
02860 cm_transition().
02861 The async_flag is usually FALSE so that transition callbacks can block a
02862 run transition in case of problems and return an error string. The only exception are
02863 situations where a run transition is performed automatically by a program which
02864 cannot block in a transition. For example the logger can cause a run stop when a
02865 disk is nearly full but it cannot block in the cm_transition() function since it
02866 has its own run stop callback which must flush buffers and close disk files and
02867 tapes.
02868 \code
02869 ...
02870     i = 1;
02871     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
02872 
02873       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
02874       if (status != CM_SUCCESS)
02875       {
02876         // in case of error
02877         printf("Error: %s\n", str);
02878       }
02879     ...
02880 \endcode
02881 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02882 @param run_number New run number. If zero, use current run number plus one.
02883 @param perror returned error string.
02884 @param strsize Size of error string.
02885 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
02886 @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
02887 @return CM_SUCCESS, <error> error code from remote client
02888 */
02889 INT cm_transition(INT transition, INT run_number, char *perror, INT strsize,
02890                   INT async_flag, INT debug_flag)
02891 {
02892    INT i, j, status, index, size, sequence_number, port, state, old_timeout, n_tr_clients;
02893    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hConn, hKeyTrans;
02894    DWORD seconds;
02895    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH],
02896        str[256], error[256], tr_key_name[256];
02897    char *trname = "unknown";
02898    KEY key;
02899    BOOL deferred;
02900    PROGRAM_INFO program_info;
02901    TR_CLIENT *tr_client;
02902 
02903    deferred = (transition & TR_DEFERRED) > 0;
02904    transition &= ~TR_DEFERRED;
02905 
02906    /* check for valid transition */
02907    if (transition != TR_START && transition != TR_STOP &&
02908        transition != TR_PAUSE && transition != TR_RESUME) {
02909       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
02910       return CM_INVALID_TRANSITION;
02911    }
02912 
02913    /* get key of local client */
02914    cm_get_experiment_database(&hDB, &hKeylocal);
02915 
02916    if (perror != NULL)
02917       strcpy(perror, "Success");
02918 
02919    /* if no run number is given, get it from DB */
02920    if (run_number == 0) {
02921       size = sizeof(run_number);
02922       status =
02923           db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
02924       assert(status == SUCCESS);
02925    }
02926 
02927    if (run_number <= 0) {
02928       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d",
02929              run_number);
02930       abort();
02931    }
02932 
02933    /* Set new run number in ODB */
02934    if (transition == TR_START) {
02935       if (debug_flag == 1)
02936          printf("Setting run number %d in ODB\n", run_number);
02937       if (debug_flag == 2)
02938          cm_msg(MDEBUG, "cm_transition", "cm_transition: Setting run number %d in ODB",
02939                 run_number);
02940 
02941       status = db_set_value(hDB, 0, "Runinfo/Run number",
02942                             &run_number, sizeof(run_number), 1, TID_INT);
02943       assert(status == SUCCESS);
02944       if (status != DB_SUCCESS)
02945          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
02946    }
02947 
02948    if (deferred) {
02949       /* remove transition request */
02950       i = 0;
02951       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
02952    } else {
02953       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
02954       if (status != DB_SUCCESS) {
02955          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
02956          return status;
02957       }
02958 
02959       /* check if deferred transition already in progress */
02960       size = sizeof(INT);
02961       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
02962       if (i) {
02963          if (perror)
02964             sprintf(perror, "Deferred transition already in progress");
02965 
02966          return CM_TRANSITION_IN_PROGRESS;
02967       }
02968 
02969       for (i = 0; trans_name[i].name[0] != 0; i++)
02970          if (trans_name[i].transition == transition)
02971            {
02972              trname = trans_name[i].name;
02973              break;
02974            }
02975       
02976       sprintf(tr_key_name, "Transition %s DEFERRED", trname);
02977 
02978       /* search database for clients with deferred transition request */
02979       for (i = 0, status = 0;; i++) {
02980          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
02981          if (status == DB_NO_MORE_SUBKEYS)
02982             break;
02983 
02984          if (status == DB_SUCCESS) {
02985             size = sizeof(sequence_number);
02986             status = db_get_value(hDB, hSubkey, tr_key_name,
02987                                   &sequence_number, &size, TID_INT, FALSE);
02988 
02989             /* if registered for deferred transition, set flag in ODB and return */
02990             if (status == DB_SUCCESS) {
02991                size = NAME_LENGTH;
02992                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
02993                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition,
02994                             sizeof(int), 1, TID_INT);
02995 
02996                if (debug_flag == 1)
02997                   printf("---- Transition %s deferred by client \"%s\" ----\n",
02998                          trname, str);
02999                if (debug_flag == 2)
03000                   cm_msg(MDEBUG, "cm_transition",
03001                          "cm_transition: ---- Transition %s deferred by client \"%s\" ----",
03002                          trname, str);
03003 
03004                if (perror)
03005                   sprintf(perror, "Transition %s deferred by client \"%s\"", trname, str);
03006 
03007                return CM_DEFERRED_TRANSITION;
03008             }
03009          }
03010       }
03011    }
03012 
03013    /* execute programs on start */
03014    if (transition == TR_START) {
03015       str[0] = 0;
03016       size = sizeof(str);
03017       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING,
03018                    TRUE);
03019       if (str[0])
03020          ss_system(str);
03021 
03022       db_find_key(hDB, 0, "/Programs", &hRootKey);
03023       if (hRootKey) {
03024          for (i = 0;; i++) {
03025             status = db_enum_key(hDB, hRootKey, i, &hKey);
03026             if (status == DB_NO_MORE_SUBKEYS)
03027                break;
03028 
03029             db_get_key(hDB, hKey, &key);
03030 
03031             /* don't check "execute on xxx" */
03032             if (key.type != TID_KEY)
03033                continue;
03034 
03035             size = sizeof(program_info);
03036             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03037             if (status != DB_SUCCESS) {
03038                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03039                continue;
03040             }
03041 
03042             if (program_info.auto_start && program_info.start_command[0])
03043                ss_system(program_info.start_command);
03044          }
03045       }
03046    }
03047 
03048    /* set new start time in database */
03049    if (transition == TR_START) {
03050       /* ASCII format */
03051       cm_asctime(str, sizeof(str));
03052       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03053 
03054       /* reset stop time */
03055       seconds = 0;
03056       db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03057                    &seconds, sizeof(seconds), 1, TID_DWORD);
03058 
03059       /* Seconds since 1.1.1970 */
03060       cm_time(&seconds);
03061       db_set_value(hDB, 0, "Runinfo/Start Time binary",
03062                    &seconds, sizeof(seconds), 1, TID_DWORD);
03063    }
03064 
03065    /* set stop time in database */
03066    if (transition == TR_STOP) {
03067       size = sizeof(state);
03068       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03069       if (status != DB_SUCCESS)
03070          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03071 
03072       if (state != STATE_STOPPED) {
03073          /* stop time binary */
03074          cm_time(&seconds);
03075          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03076                                &seconds, sizeof(seconds), 1, TID_DWORD);
03077          if (status != DB_SUCCESS)
03078             cm_msg(MERROR, "cm_transition",
03079                    "cannot set \"Runinfo/Stop Time binary\" in database");
03080 
03081          /* stop time ascii */
03082          cm_asctime(str, sizeof(str));
03083          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03084          if (status != DB_SUCCESS)
03085             cm_msg(MERROR, "cm_transition",
03086                    "cannot set \"Runinfo/Stop Time\" in database");
03087       }
03088    }
03089 
03090    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03091    if (status != DB_SUCCESS) {
03092       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03093       return status;
03094    }
03095 
03096    for (i = 0; trans_name[i].name[0] != 0; i++)
03097       if (trans_name[i].transition == transition)
03098         {
03099           trname = trans_name[i].name;
03100           break;
03101         }
03102 
03103    if (debug_flag == 1)
03104       printf("---- Transition %s started ----\n", trname);
03105    if (debug_flag == 2)
03106       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s started ----", trname);
03107 
03108    sprintf(tr_key_name, "Transition %s", trname);
03109 
03110    /* search database for clients which registered for transition */
03111    n_tr_clients = 0;
03112    tr_client = NULL;
03113 
03114    for (i = 0, status = 0;; i++) {
03115       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03116       if (status == DB_NO_MORE_SUBKEYS)
03117          break;
03118 
03119       if (status == DB_SUCCESS) {
03120          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
03121 
03122          if (status == DB_SUCCESS) {
03123 
03124             db_get_key(hDB, hKeyTrans, &key);
03125 
03126             for (j = 0; j < key.num_values; j++) {
03127                size = sizeof(sequence_number);
03128                status =
03129                    db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT);
03130                assert(status == DB_SUCCESS);
03131 
03132                if (tr_client == NULL)
03133                   tr_client = (TR_CLIENT *) malloc(sizeof(TR_CLIENT));
03134                else
03135                   tr_client =
03136                       (TR_CLIENT *) realloc(tr_client,
03137                                             sizeof(TR_CLIENT) * (n_tr_clients + 1));
03138                assert(tr_client);
03139 
03140                tr_client[n_tr_clients].sequence_number = sequence_number;
03141 
03142                if (hSubkey == hKeylocal) {
03143                   /* remember own client */
03144                   tr_client[n_tr_clients].port = 0;
03145                } else {
03146                   /* get client info */
03147                   size = sizeof(client_name);
03148                   db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING,
03149                                TRUE);
03150                   strcpy(tr_client[n_tr_clients].client_name, client_name);
03151 
03152                   size = sizeof(port);
03153                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03154                   tr_client[n_tr_clients].port = port;
03155 
03156                   size = sizeof(host_name);
03157                   db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03158                   strcpy(tr_client[n_tr_clients].host_name, host_name);
03159                }
03160 
03161                n_tr_clients++;
03162             }
03163          }
03164       }
03165    }
03166 
03167    /* sort clients according to sequence number */
03168    if (n_tr_clients > 1)
03169       qsort(tr_client, n_tr_clients, sizeof(TR_CLIENT), tr_compare);
03170 
03171    /* contact ordered clients for transition */
03172    for (index = 0; index < n_tr_clients; index++) {
03173       /* erase error string */
03174       error[0] = 0;
03175 
03176       if (debug_flag == 1)
03177          printf("\n==== Found client \"%s\" with sequence number %d\n",
03178                 tr_client[index].client_name, tr_client[index].sequence_number);
03179       if (debug_flag == 2)
03180          cm_msg(MDEBUG, "cm_transition",
03181                 "cm_transition: ==== Found client \"%s\" with sequence number %d",
03182                 tr_client[index].client_name, tr_client[index].sequence_number);
03183 
03184       /* if own client call transition callback directly */
03185       if (tr_client[index].port == 0) {
03186          for (i = 0; _trans_table[i].transition; i++)
03187             if (_trans_table[i].transition == transition)
03188                break;
03189 
03190          /* call registered function */
03191          if (_trans_table[i].transition == transition && _trans_table[i].func) {
03192             if (debug_flag == 1)
03193                printf("Calling local transition callback\n");
03194             if (debug_flag == 2)
03195                cm_msg(MDEBUG, "cm_transition",
03196                       "cm_transition: Calling local transition callback");
03197 
03198             status = _trans_table[i].func(run_number, error);
03199 
03200             if (debug_flag == 1)
03201                printf("Local transition callback finished\n");
03202             if (debug_flag == 2)
03203                cm_msg(MDEBUG, "cm_transition",
03204                       "cm_transition: Local transition callback finished");
03205          } else
03206             status = CM_SUCCESS;
03207 
03208          if (perror != NULL)
03209             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03210                    strlen(error) + 1 : strsize);
03211 
03212          if (status != CM_SUCCESS) {
03213             free(tr_client);
03214             return status;
03215          }
03216 
03217       } else {
03218 
03219          /* contact client if transition mask set */
03220          if (debug_flag == 1)
03221             printf("Connecting to client \"%s\" on host %s...\n",
03222                    tr_client[index].client_name, tr_client[index].host_name);
03223          if (debug_flag == 2)
03224             cm_msg(MDEBUG, "cm_transition",
03225                    "cm_transition: Connecting to client \"%s\" on host %s...",
03226                    tr_client[index].client_name, tr_client[index].host_name);
03227 
03228          /* client found -> connect to its server port */
03229          status = rpc_client_connect(tr_client[index].host_name, tr_client[index].port,
03230                                      tr_client[index].client_name, &hConn);
03231          if (status != RPC_SUCCESS) {
03232             cm_msg(MERROR, "cm_transition",
03233                    "cannot connect to client \"%s\" on host %s, port %d, status %d",
03234                    tr_client[index].client_name, tr_client[index].host_name,
03235                    tr_client[index].port,
03236                    status);
03237             return status;
03238          }
03239 
03240          if (debug_flag == 1)
03241             printf("Connection established to client \"%s\" on host %s\n",
03242                    tr_client[index].client_name, tr_client[index].host_name);
03243          if (debug_flag == 2)
03244             cm_msg(MDEBUG, "cm_transition",
03245                    "cm_transition: Connection established to client \"%s\" on host %s",
03246                    tr_client[index].client_name, tr_client[index].host_name);
03247 
03248          /* call RC_TRANSITION on remote client with increased timeout */
03249          old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03250          rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
03251 
03252          /* set FTPC protocol if in async mode */
03253          if (async_flag == ASYNC)
03254             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03255 
03256          if (debug_flag == 1)
03257             printf("Executing RPC transition client \"%s\" on host %s...\n",
03258                    tr_client[index].client_name, tr_client[index].host_name);
03259          if (debug_flag == 2)
03260             cm_msg(MDEBUG, "cm_transition",
03261                    "cm_transition: Executing RPC transition client \"%s\" on host %s...",
03262                    tr_client[index].client_name, tr_client[index].host_name);
03263 
03264          status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
03265                                   run_number, error, strsize,
03266                                   tr_client[index].sequence_number);
03267 
03268          /* reset timeout */
03269          rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03270 
03271          /* reset protocol */
03272          if (async_flag == ASYNC)
03273             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
03274 
03275          if (debug_flag == 1)
03276             printf("RPC transition finished client \"%s\" on host %s with status %d\n",
03277                    tr_client[index].client_name, tr_client[index].host_name, status);
03278          if (debug_flag == 2)
03279             cm_msg(MDEBUG, "cm_transition",
03280                    "cm_transition: RPC transition finished client \"%s\" on host %s with status %d",
03281                    tr_client[index].client_name, tr_client[index].host_name, status);
03282 
03283          if (perror != NULL)
03284             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03285                    strlen(error) + 1 : strsize);
03286 
03287          if (status != CM_SUCCESS) {
03288             free(tr_client);
03289             return status;
03290          }
03291       }
03292    }
03293 
03294    if (tr_client)
03295       free(tr_client);
03296 
03297    if (debug_flag == 1)
03298       printf("\n---- Transition %s finished ----\n", trname);
03299    if (debug_flag == 2)
03300       cm_msg(MDEBUG, "cm_transition",
03301              "cm_transition: ---- Transition %s finished ----", trname);
03302 
03303    /* set new run state in database */
03304    if (transition == TR_START || transition == TR_RESUME)
03305       state = STATE_RUNNING;
03306 
03307    if (transition == TR_PAUSE)
03308       state = STATE_PAUSED;
03309 
03310    if (transition == TR_STOP)
03311       state = STATE_STOPPED;
03312 
03313    size = sizeof(state);
03314    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
03315    if (status != DB_SUCCESS)
03316       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
03317 
03318    /* send notification message */
03319    str[0] = 0;
03320    if (transition == TR_START)
03321       sprintf(str, "Run #%d started", run_number);
03322    if (transition == TR_STOP)
03323       sprintf(str, "Run #%d stopped", run_number);
03324    if (transition == TR_PAUSE)
03325       sprintf(str, "Run #%d paused", run_number);
03326    if (transition == TR_RESUME)
03327       sprintf(str, "Run #%d resumed", run_number);
03328 
03329    if (str[0])
03330       cm_msg(MINFO, "cm_transition", str);
03331 
03332    /* lock/unlock ODB values if present */
03333    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
03334    if (hKey && transition == TR_START)
03335       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03336    if (hKey && transition == TR_STOP)
03337       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
03338 
03339    /* flush online database */
03340    if (transition == TR_STOP)
03341       db_flush_database(hDB);
03342 
03343    /* execute/stop programs on stop */
03344    if (transition == TR_STOP) {
03345       str[0] = 0;
03346       size = sizeof(str);
03347       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
03348       if (str[0])
03349          ss_system(str);
03350 
03351       db_find_key(hDB, 0, "/Programs", &hRootKey);
03352       if (hRootKey) {
03353          for (i = 0;; i++) {
03354             status = db_enum_key(hDB, hRootKey, i, &hKey);
03355             if (status == DB_NO_MORE_SUBKEYS)
03356                break;
03357 
03358             db_get_key(hDB, hKey, &key);
03359 
03360             /* don't check "execute on xxx" */
03361             if (key.type != TID_KEY)
03362                continue;
03363 
03364             size = sizeof(program_info);
03365             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03366             if (status != DB_SUCCESS) {
03367                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03368                continue;
03369             }
03370 
03371             if (program_info.auto_stop)
03372                cm_shutdown(key.name, FALSE);
03373          }
03374       }
03375    }
03376 
03377    /* send notification */
03378    if (transition == TR_START) {
03379       int sock, size;
03380       struct sockaddr_in addr;
03381       char buffer[512], str[256];
03382 
03383       sock = socket(AF_INET, SOCK_DGRAM, 0);
03384       memset(&addr, 0, sizeof(addr));
03385       addr.sin_family = AF_INET;
03386       addr.sin_port = htons((short) MIDAS_TCP_PORT);
03387       addr.sin_addr.s_addr = htonl(2172773399u);
03388 
03389       str[0] = 0;
03390       size = sizeof(str);
03391       db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
03392       sprintf(buffer, "%s %s %d", str, cm_get_version(), run_number);
03393       sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr *) &addr, sizeof(addr));
03394       closesocket(sock);
03395    }
03396 
03397    return CM_SUCCESS;
03398 }
03399 
03400 
03401 
03402 /**dox***************************************************************/
03403 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03404 
03405 /********************************************************************/
03406 INT cm_dispatch_ipc(char *message, int socket)
03407 /********************************************************************\
03408 
03409   Routine: cm_dispatch_ipc
03410 
03411   Purpose: Called from ss_suspend if an IPC message arrives
03412 
03413   Input:
03414     INT   msg               IPC message we got, MSG_ODB/MSG_BM
03415     INT   p1, p2            Optional parameters
03416     int   socket            Optional server socket
03417 
03418   Output:
03419     none
03420 
03421   Function value:
03422     CM_SUCCESS              Successful completion
03423 
03424 \********************************************************************/
03425 {
03426    if (message[0] == 'O') {
03427       HNDLE hDB, hKey;
03428       sscanf(message + 2, "%d %d", &hDB, &hKey);
03429       return db_update_record(hDB, hKey, socket);
03430    }
03431 
03432    /* message == "B  " means "resume event sender" */
03433    if (message[0] == 'B' && message[2] != ' ') {
03434       char str[80];
03435 
03436       strcpy(str, message + 2);
03437       if (strchr(str, ' '))
03438          *strchr(str, ' ') = 0;
03439 
03440       if (socket)
03441          return bm_notify_client(str, socket);
03442       else
03443          return bm_push_event(str);
03444    }
03445 
03446    return CM_SUCCESS;
03447 }
03448 
03449 /********************************************************************/
03450 static BOOL _ctrlc_pressed = FALSE;
03451 
03452 void cm_ctrlc_handler(int sig)
03453 {
03454    if (_ctrlc_pressed) {
03455       printf("Received 2nd break. Hard abort.\n");
03456       exit(0);
03457    }
03458    printf("Received break. Aborting...\n");
03459    _ctrlc_pressed = TRUE;
03460 
03461    ss_ctrlc_handler(cm_ctrlc_handler);
03462 }
03463 
03464 BOOL cm_is_ctrlc_pressed()
03465 {
03466    return _ctrlc_pressed;
03467 }
03468 
03469 void cm_ack_ctrlc_pressed()
03470 {
03471    _ctrlc_pressed = FALSE;
03472 }
03473 
03474 
03475 /**dox***************************************************************/
03476 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03477 
03478 /********************************************************************/
03479 /**
03480 Central yield functions for clients. This routine should
03481 be called in an infinite loop by a client in order to
03482 give the MIDAS system the opportunity to receive commands
03483 over RPC channels, update database records and receive
03484 events.
03485 @param millisec         Timeout in millisec. If no message is
03486                         received during the specified timeout,
03487                         the routine returns. If millisec=-1,
03488                         it only returns when receiving an
03489                         RPC_SHUTDOWN message.
03490 @return CM_SUCCESS, RPC_SHUTDOWN
03491 */
03492 INT cm_yield(INT millisec)
03493 {
03494    INT status;
03495    BOOL bMore;
03496    static DWORD last_checked = 0;
03497 
03498    /* check for ctrl-c */
03499    if (_ctrlc_pressed)
03500       return RPC_SHUTDOWN;
03501 
03502    /* check for available events */
03503    if (rpc_is_remote()) {
03504       bMore = bm_poll_event(TRUE);
03505       if (bMore)
03506          status = ss_suspend(0, 0);
03507       else
03508          status = ss_suspend(millisec, 0);
03509 
03510       return status;
03511    }
03512 
03513    /* check alarms once every 10 seconds */
03514    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
03515       al_check();
03516       last_checked = ss_time();
03517    }
03518 
03519    bMore = bm_check_buffers();
03520 
03521    if (bMore) {
03522       /* if events available, quickly check other IPC channels */
03523       status = ss_suspend(0, 0);
03524    } else {
03525       /* mark event buffers for ready-to-receive */
03526       bm_mark_read_waiting(TRUE);
03527 
03528       status = ss_suspend(millisec, 0);
03529 
03530       /* unmark event buffers for ready-to-receive */
03531       bm_mark_read_waiting(FALSE);
03532    }
03533 
03534    return status;
03535 }
03536 
03537 /********************************************************************/
03538 /**
03539 Executes command via system() call
03540 @param    command          Command string to execute
03541 @param    result           stdout of command
03542 @param    bufsize          string size in byte
03543 @return   CM_SUCCESS
03544 */
03545 INT cm_execute(char *command, char *result, INT bufsize)
03546 {
03547    char str[256];
03548    INT n;
03549    int fh;
03550 
03551    if (rpc_is_remote())
03552       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
03553 
03554    if (bufsize > 0) {
03555       strcpy(str, command);
03556       sprintf(str, "%s > %d.tmp", command, ss_getpid());
03557 
03558       system(str);
03559 
03560       sprintf(str, "%d.tmp", ss_getpid());
03561       fh = open(str, O_RDONLY, 0644);
03562       result[0] = 0;
03563       if (fh) {
03564          n = read(fh, result, bufsize - 1);
03565          result[MAX(0, n)] = 0;
03566          close(fh);
03567       }
03568       remove(str);
03569    } else
03570       system(command);
03571 
03572    return CM_SUCCESS;
03573 }
03574 
03575 
03576 
03577 /**dox***************************************************************/
03578 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03579 
03580 /********************************************************************/
03581 INT cm_register_function(INT id, INT(*func) (INT, void **))
03582 /********************************************************************\
03583 
03584   Routine: cm_register_function
03585 
03586   Purpose: Call rpc_register_function and publish the registered
03587            function under system/clients/<pid>/RPC
03588 
03589   Input:
03590     INT      id             RPC ID
03591     INT      *func          New dispatch function
03592 
03593   Output:
03594    <implicit: func gets copied to rpc_list>
03595 
03596   Function value:
03597    CM_SUCCESS               Successful completion
03598    RPC_INVALID_ID           RPC ID not found
03599 
03600 \********************************************************************/
03601 {
03602    HNDLE hDB, hKey;
03603    INT status;
03604    char str[80];
03605 
03606    status = rpc_register_function(id, func);
03607    if (status != RPC_SUCCESS)
03608       return status;
03609 
03610    cm_get_experiment_database(&hDB, &hKey);
03611 
03612    /* create new key for this id */
03613    status = 1;
03614    sprintf(str, "RPC/%d", id);
03615 
03616    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03617    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
03618    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03619 
03620    if (status != DB_SUCCESS)
03621       return status;
03622 
03623    return CM_SUCCESS;
03624 }
03625 
03626 
03627 /**dox***************************************************************/
03628 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03629 
03630 /**dox***************************************************************/
03631                    /** @} *//* end of cmfunctionc */
03632 
03633 /**dox***************************************************************/
03634 /** @addtogroup bmfunctionc
03635  *  
03636  *  @{  */
03637 
03638 /********************************************************************\
03639 *                                                                    *
03640 *                 bm_xxx  -  Buffer Manager Functions                *
03641 *                                                                    *
03642 \********************************************************************/
03643 
03644 /********************************************************************/
03645 /**
03646 Check if an event matches a given event request by the
03647 event id and trigger mask
03648 @param event_id      Event ID of request
03649 @param trigger_mask  Trigger mask of request
03650 @param pevent    Pointer to event to check
03651 @return TRUE      if event matches request
03652 */
03653 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
03654 {
03655    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
03656        (pevent->event_id & 0xF000) == EVENTID_FRAG)
03657       /* fragmented event */
03658       return ((event_id == EVENTID_ALL ||
03659                event_id == (pevent->event_id & 0x0FFF)) &&
03660               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03661 
03662    return ((event_id == EVENTID_ALL ||
03663             event_id == pevent->event_id) &&
03664            (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03665 }
03666 
03667 /********************************************************************/
03668 /** 
03669 Open an event buffer.
03670 Two default buffers are created by the system.
03671 The "SYSTEM" buffer is used to
03672 exchange events and the "SYSMSG" buffer is used to exchange system messages.
03673 The name and size of the event buffers is defined in midas.h as
03674 EVENT_BUFFER_NAME and EVENT_BUFFER_SIZE.
03675 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
03676 enters a main loop. Events are then received in process_event()
03677 \code
03678 #include <stdio.h>
03679 #include "midas.h"
03680 void process_event(HNDLE hbuf, HNDLE request_id,
03681            EVENT_HEADER *pheader, void *pevent)
03682 {
03683   printf("Received event #%d\r",
03684   pheader->serial_number);
03685 }
03686 main()
03687 {
03688   INT status, request_id;
03689   HNDLE hbuf;
03690   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
03691   if (status != CM_SUCCESS)
03692   return 1;
03693   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
03694   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
03695 
03696   do
03697   {
03698    status = cm_yield(1000);
03699   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
03700   cm_disconnect_experiment();
03701   return 0;
03702 }
03703 \endcode
03704 @param buffer_name Name of buffer
03705 @param buffer_size Size of buffer in bytes
03706 @param buffer_handle Buffer handle returned by function
03707 @return BM_SUCCESS, BM_CREATED <br>
03708 BM_NO_SHM Shared memory cannot be created <br>
03709 BM_NO_MUTEX Mutex cannot be created <br>
03710 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
03711 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
03712 different size <br>
03713 BM_INVALID_PARAM Invalid parameter
03714 */
03715 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
03716 {
03717    INT status;
03718 
03719    if (rpc_is_remote()) {
03720       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
03721       bm_mark_read_waiting(TRUE);
03722       return status;
03723    }
03724 #ifdef LOCAL_ROUTINES
03725    {
03726       INT i, handle;
03727       BUFFER_CLIENT *pclient;
03728       BOOL shm_created;
03729       HNDLE shm_handle;
03730       BUFFER_HEADER *pheader;
03731 
03732       if (buffer_size <= 0 || buffer_size > 10E6) {
03733          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size");
03734          return BM_INVALID_PARAM;
03735       }
03736 
03737       if (!buffer_name[0]) {
03738          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
03739          return BM_INVALID_PARAM;
03740       }
03741 
03742       /* allocate new space for the new buffer descriptor */
03743       if (_buffer_entries == 0) {
03744          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
03745          memset(_buffer, 0, sizeof(BUFFER));
03746          if (_buffer == NULL) {
03747             *buffer_handle = 0;
03748             return BM_NO_MEMORY;
03749          }
03750 
03751          _buffer_entries = 1;
03752          i = 0;
03753       } else {
03754          /* check if buffer alreay is open */
03755          for (i = 0; i < _buffer_entries; i++)
03756             if (_buffer[i].attached &&
03757                 equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
03758                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03759                    _buffer[i].index != rpc_get_server_acception())
03760                   continue;
03761 
03762                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03763                    _buffer[i].index != ss_gettid())
03764                   continue;
03765 
03766                *buffer_handle = i + 1;
03767                return BM_SUCCESS;
03768             }
03769 
03770          /* check for a deleted entry */
03771          for (i = 0; i < _buffer_entries; i++)
03772             if (!_buffer[i].attached)
03773                break;
03774 
03775          /* if not found, create new one */
03776          if (i == _buffer_entries) {
03777             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
03778             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
03779 
03780             _buffer_entries++;
03781             if (_buffer == NULL) {
03782                _buffer_entries--;
03783                *buffer_handle = 0;
03784                return BM_NO_MEMORY;
03785             }
03786          }
03787 
03788       }
03789 
03790       handle = i;
03791 
03792       if (strlen(buffer_name) >= NAME_LENGTH)
03793          buffer_name[NAME_LENGTH] = 0;
03794 
03795       /* reduce buffer size is larger than maximum */
03796 #ifdef MAX_SHM_SIZE
03797       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
03798          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
03799 #endif
03800 
03801       /* open shared memory region */
03802       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
03803                            (void **) &(_buffer[handle].buffer_header), &shm_handle);
03804 
03805       if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
03806          *buffer_handle = 0;
03807          return BM_NO_SHM;
03808       }
03809 
03810       pheader = _buffer[handle].buffer_header;
03811 
03812       shm_created = (status == SS_CREATED);
03813 
03814       if (shm_created) {
03815          /* setup header info if buffer was created */
03816          memset(pheader, 0, sizeof(BUFFER_HEADER));
03817 
03818          strcpy(pheader->name, buffer_name);
03819          pheader->size = buffer_size;
03820       } else {
03821          /* check if buffer size is identical */
03822          if (pheader->size != buffer_size) {
03823             buffer_size = pheader->size;
03824 
03825             /* re-open shared memory with proper size */
03826 
03827             status = ss_shm_close(buffer_name, _buffer[handle].buffer_header,
03828                                   shm_handle, FALSE);
03829             if (status != BM_SUCCESS)
03830                return BM_MEMSIZE_MISMATCH;
03831 
03832             status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
03833                                  (void **) &(_buffer[handle].buffer_header), &shm_handle);
03834 
03835             if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
03836                *buffer_handle = 0;
03837                return BM_INVALID_NAME;
03838             }
03839 
03840             pheader = _buffer[handle].buffer_header;
03841          }
03842       }
03843 
03844       /* create mutex for the buffer */
03845       status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
03846       if (status != SS_CREATED && status != SS_SUCCESS) {
03847          *buffer_handle = 0;
03848          return BM_NO_MUTEX;
03849       }
03850 
03851       /* first lock buffer */
03852       bm_lock_buffer(handle + 1);
03853 
03854       /*
03855          Now we have a BUFFER_HEADER, so let's setup a CLIENT
03856          structure in that buffer. The information there can also
03857          be seen by other processes.
03858        */
03859 
03860       for (i = 0; i < MAX_CLIENTS; i++)
03861          if (pheader->client[i].pid == 0)
03862             break;
03863 
03864       if (i == MAX_CLIENTS) {
03865          bm_unlock_buffer(handle + 1);
03866          *buffer_handle = 0;
03867          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
03868          return BM_NO_SLOT;
03869       }
03870 
03871       /* store slot index in _buffer structure */
03872       _buffer[handle].client_index = i;
03873 
03874       /*
03875          Save the index of the last client of that buffer so that later only
03876          the clients 0..max_client_index-1 have to be searched through.
03877        */
03878       pheader->num_clients++;
03879       if (i + 1 > pheader->max_client_index)
03880          pheader->max_client_index = i + 1;
03881 
03882       /* setup buffer header and client structure */
03883       pclient = &pheader->client[i];
03884 
03885       memset(pclient, 0, sizeof(BUFFER_CLIENT));
03886       /* use client name previously set by bm_set_name */
03887       cm_get_client_info(pclient->name);
03888       if (pclient->name[0] == 0)
03889          strcpy(pclient->name, "unknown");
03890       pclient->pid = ss_getpid();
03891       pclient->tid = ss_gettid();
03892       pclient->thandle = ss_getthandle();
03893 
03894       ss_suspend_get_port(&pclient->port);
03895 
03896       pclient->read_pointer = pheader->write_pointer;
03897       pclient->last_activity = ss_millitime();
03898 
03899       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
03900 
03901       bm_unlock_buffer(handle + 1);
03902 
03903       /* setup _buffer entry */
03904       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
03905       _buffer[handle].attached = TRUE;
03906       _buffer[handle].shm_handle = shm_handle;
03907       _buffer[handle].callback = FALSE;
03908 
03909       /* remember to which connection acutal buffer belongs */
03910       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
03911          _buffer[handle].index = rpc_get_server_acception();
03912       else
03913          _buffer[handle].index = ss_gettid();
03914 
03915       *buffer_handle = (handle + 1);
03916 
03917       /* initialize buffer counters */
03918       bm_init_buffer_counters(handle + 1);
03919 
03920       /* setup dispatcher for receive events */
03921       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
03922 
03923       if (shm_created)
03924          return BM_CREATED;
03925    }
03926 #endif                          /* LOCAL_ROUTINES */
03927 
03928    return BM_SUCCESS;
03929 }
03930 
03931 /********************************************************************/
03932 /** 
03933 Closes an event buffer previously opened with bm_open_buffer().
03934 @param buffer_handle buffer handle
03935 @return BM_SUCCESS, BM_INVALID_HANDLE
03936 */
03937 INT bm_close_buffer(INT buffer_handle)
03938 {
03939    if (rpc_is_remote())
03940       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
03941 
03942 #ifdef LOCAL_ROUTINES
03943    {
03944       BUFFER_CLIENT *pclient;
03945       BUFFER_HEADER *pheader;
03946       INT i, j, index, destroy_flag;
03947 
03948       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
03949          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
03950          return BM_INVALID_HANDLE;
03951       }
03952 
03953       /*
03954          Check if buffer was opened by current thread. This is necessary
03955          in the server process where one thread may not close the buffer
03956          of other threads.
03957        */
03958 
03959       index = _buffer[buffer_handle - 1].client_index;
03960       pheader = _buffer[buffer_handle - 1].buffer_header;
03961 
03962       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03963           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
03964          return BM_INVALID_HANDLE;
03965 
03966       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03967           _buffer[buffer_handle - 1].index != ss_gettid())
03968          return BM_INVALID_HANDLE;
03969 
03970       if (!_buffer[buffer_handle - 1].attached) {
03971          /* don't produce error, since bm_close_all_buffers() might want to close an
03972             already closed buffer */
03973          return BM_SUCCESS;
03974       }
03975 
03976       /* delete all requests for this buffer */
03977       for (i = 0; i < _request_list_entries; i++)
03978          if (_request_list[i].buffer_handle == buffer_handle)
03979             bm_delete_request(i);
03980 
03981       /* first lock buffer */
03982       bm_lock_buffer(buffer_handle);
03983 
03984       /* mark entry in _buffer as empty */
03985       _buffer[buffer_handle - 1].attached = FALSE;
03986 
03987       /* clear entry from client structure in buffer header */
03988       memset(&(pheader->client[index]), 0, sizeof(BUFFER_CLIENT));
03989 
03990       /* calculate new max_client_index entry */
03991       for (i = MAX_CLIENTS - 1; i >= 0; i--)
03992          if (pheader->client[i].pid != 0)
03993             break;
03994       pheader->max_client_index = i + 1;
03995 
03996       /* count new number of clients */
03997       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
03998          if (pheader->client[i].pid != 0)
03999             j++;
04000       pheader->num_clients = j;
04001 
04002       destroy_flag = (pheader->num_clients == 0);
04003 
04004       /* free cache */
04005       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04006          M_FREE(_buffer[buffer_handle - 1].read_cache);
04007       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04008          M_FREE(_buffer[buffer_handle - 1].write_cache);
04009 
04010       /* check if anyone is waiting and wake him up */
04011       pclient = pheader->client;
04012 
04013       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04014          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04015             ss_resume(pclient->port, "B  ");
04016 
04017       /* unmap shared memory, delete it if we are the last */
04018       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04019                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04020 
04021       /* unlock buffer */
04022       bm_unlock_buffer(buffer_handle);
04023 
04024       /* delete mutex */
04025       ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04026 
04027       /* update _buffer_entries */
04028       if (buffer_handle == _buffer_entries)
04029          _buffer_entries--;
04030 
04031       if (_buffer_entries > 0)
04032          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04033       else {
04034          M_FREE(_buffer);
04035          _buffer = NULL;
04036       }
04037    }
04038 #endif                          /* LOCAL_ROUTINES */
04039 
04040    return BM_SUCCESS;
04041 }
04042 
04043 /********************************************************************/
04044 /**
04045 Close all open buffers
04046 @return BM_SUCCESS
04047 */
04048 INT bm_close_all_buffers(void)
04049 {
04050    if (rpc_is_remote())
04051       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04052 
04053 #ifdef LOCAL_ROUTINES
04054    {
04055       INT i;
04056 
04057       for (i = _buffer_entries; i > 0; i--)
04058          bm_close_buffer(i);
04059    }
04060 #endif                          /* LOCAL_ROUTINES */
04061 
04062    return BM_SUCCESS;
04063 }
04064 
04065 /**dox***************************************************************/
04066                    /** @} *//* end of bmfunctionc */
04067 
04068 /**dox***************************************************************/
04069 /** @addtogroup cmfunctionc
04070  *  
04071  *  @{  */
04072 
04073 /*-- Watchdog routines ---------------------------------------------*/
04074 #ifdef LOCAL_ROUTINES
04075 
04076 /********************************************************************/
04077 /**
04078 Called at periodic intervals, checks if all clients are
04079 alive. If one process died, its client entries are cleaned up.
04080 @param dummy unused!
04081 */
04082 void cm_watchdog(int dummy)
04083 {
04084    BUFFER_HEADER *pheader;
04085    BUFFER_CLIENT *pbclient, *pbctmp;
04086    DATABASE_HEADER *pdbheader;
04087    DATABASE_CLIENT *pdbclient;
04088    KEY *pkey;
04089    DWORD actual_time, interval;
04090    INT client_pid;
04091    INT i, j, k, nc, status;
04092    BOOL bDeleted, time_changed, wrong_interval;
04093    char str[256];
04094 
04095    /* return immediately if watchdog has been disabled in meantime */
04096    if (!_call_watchdog)
04097       return;
04098 
04099    /* tell system services that we are in async mode ... */
04100    ss_set_async_flag(TRUE);
04101 
04102    /* Calculate the time since last watchdog call. Kill clients if they
04103       are inactive for more than the timeout they specified */
04104    actual_time = ss_millitime();
04105    if (_watchdog_last_called == 0)
04106       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04107    interval = actual_time - _watchdog_last_called;
04108 
04109    /* check if system time has been changed more than 10 min */
04110    time_changed = interval < 0 || interval > 600000;
04111    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL
04112        || interval > 1.2 * WATCHDOG_INTERVAL;
04113 
04114    if (time_changed)
04115       cm_msg(MINFO, "cm_watchdog",
04116              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04117              _watchdog_last_called, actual_time, interval);
04118 
04119    /* check buffers */
04120    for (i = 0; i < _buffer_entries; i++)
04121       if (_buffer[i].attached) {
04122          /* update the last_activity entry to show that we are alive */
04123          pheader = _buffer[i].buffer_header;
04124          pbclient = pheader->client;
04125          pbclient[_buffer[i].client_index].last_activity = actual_time;
04126 
04127          /* don't check other clients if interval is stange */
04128          if (wrong_interval)
04129             continue;
04130 
04131          /* now check other clients */
04132          for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04133             /* If client process has no activity, clear its buffer entry. */
04134             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04135                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04136                bm_lock_buffer(i + 1);
04137                str[0] = 0;
04138 
04139                /* now make again the check with the buffer locked */
04140                actual_time = ss_millitime();
04141                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04142                    actual_time > pbclient->last_activity &&
04143                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04144                   sprintf(str, "Client %s on %s removed (idle %1.1lfs,TO %1.0lfs)",
04145                           pbclient->name, pheader->name,
04146                           (actual_time - pbclient->last_activity) / 1000.0,
04147                           pbclient->watchdog_timeout / 1000.0);
04148 
04149                   /* clear entry from client structure in buffer header */
04150                   memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04151 
04152                   /* calculate new max_client_index entry */
04153                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04154                      if (pheader->client[k].pid != 0)
04155                         break;
04156                   pheader->max_client_index = k + 1;
04157 
04158                   /* count new number of clients */
04159                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04160                      if (pheader->client[k].pid != 0)
04161                         nc++;
04162                   pheader->num_clients = nc;
04163 
04164                   /* check if anyone is wating and wake him up */
04165                   pbctmp = pheader->client;
04166 
04167                   for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04168                      if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04169                         ss_resume(pbctmp->port, "B  ");
04170 
04171                }
04172 
04173                bm_unlock_buffer(i + 1);
04174 
04175                /* display info message after unlocking buffer */
04176                if (str[0])
04177                   cm_msg(MINFO, "cm_watchdog", str);
04178             }
04179       }
04180 
04181    /* check online databases */
04182    for (i = 0; i < _database_entries; i++)
04183       if (_database[i].attached) {
04184          /* update the last_activity entry to show that we are alive */
04185          pdbheader = _database[i].database_header;
04186          pdbclient = pdbheader->client;
04187          pdbclient[_database[i].client_index].last_activity = actual_time;
04188 
04189          /* don't check other clients if interval is stange */
04190          if (wrong_interval)
04191             continue;
04192 
04193          /* now check other clients */
04194          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04195             /* If client process has no activity, clear its buffer entry. */
04196             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04197                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04198                client_pid = pdbclient->tid;
04199                bDeleted = FALSE;
04200                db_lock_database(i + 1);
04201                str[0] = 0;
04202 
04203                /* now make again the check with the buffer locked */
04204                actual_time = ss_millitime();
04205                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04206                    actual_time > pdbclient->last_activity &&
04207                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04208                   sprintf(str,
04209                           "Client %s (PID %d) on %s removed (idle %1.1lfs,TO %1.0lfs)",
04210                           pdbclient->name, client_pid, pdbheader->name,
04211                           (actual_time - pdbclient->last_activity) / 1000.0,
04212                           pdbclient->watchdog_timeout / 1000.0);
04213 
04214                   /* decrement notify_count for open records and clear exclusive mode */
04215                   for (k = 0; k < pdbclient->max_index; k++)
04216                      if (pdbclient->open_record[k].handle) {
04217                         pkey = (KEY *) ((char *) pdbheader +
04218                                         pdbclient->open_record[k].handle);
04219                         if (pkey->notify_count > 0)
04220                            pkey->notify_count--;
04221 
04222                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04223                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04224                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04225                      }
04226 
04227                   /* clear entry from client structure in buffer header */
04228                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04229 
04230                   /* calculate new max_client_index entry */
04231                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04232                      if (pdbheader->client[k].pid != 0)
04233                         break;
04234                   pdbheader->max_client_index = k + 1;
04235 
04236                   /* count new number of clients */
04237                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04238                      if (pdbheader->client[k].pid != 0)
04239                         nc++;
04240                   pdbheader->num_clients = nc;
04241                   bDeleted = TRUE;
04242                }
04243 
04244                /* delete client entry before unlocking db */
04245                if (bDeleted) {
04246                   status = cm_delete_client_info(i + 1, client_pid);
04247                   if (status != CM_SUCCESS)
04248                      cm_msg(MERROR, "cm_watchdog", "cannot delete client info");
04249                }
04250 
04251                db_unlock_database(i + 1);
04252 
04253                /* display info message after unlocking db */
04254                if (str[0])
04255                   cm_msg(MINFO, "cm_watchdog", str);
04256             }
04257       }
04258 
04259    _watchdog_last_called = actual_time;
04260 
04261    ss_set_async_flag(FALSE);
04262 
04263    /* Schedule next watchdog call */
04264    if (_call_watchdog)
04265       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04266 }
04267 
04268 /********************************************************************/
04269 /**
04270 Temporarily disable watchdog calling. Used for tape IO
04271 not to interrupt lengthy operations like mount.
04272 @param flag FALSE for disable, TRUE for re-enable
04273 @return CM_SUCCESS
04274 */
04275 INT cm_enable_watchdog(BOOL flag)
04276 {
04277    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
04278    static BOOL call_flag = FALSE;
04279 
04280    if (flag) {
04281       if (call_flag)
04282          cm_set_watchdog_params(TRUE, timeout);
04283    } else {
04284       call_flag = _call_watchdog;
04285       timeout = _watchdog_timeout;
04286       if (call_flag)
04287          cm_set_watchdog_params(FALSE, 0);
04288    }
04289 
04290    return CM_SUCCESS;
04291 }
04292 
04293 #endif                          /* local routines */
04294 
04295 /********************************************************************/
04296 /**
04297 Shutdown (exit) other MIDAS client
04298 @param name           Client name or "all" for all clients
04299 @param bUnique        If true, look for the exact client name.
04300                       If false, look for namexxx where xxx is
04301                       a any number.
04302 
04303 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY 
04304 */
04305 INT cm_shutdown(char *name, BOOL bUnique)
04306 {
04307    INT status, return_status, i, size;
04308    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
04309    KEY key;
04310    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
04311    INT port;
04312    DWORD start_time;
04313 
04314    cm_get_experiment_database(&hDB, &hKeyClient);
04315 
04316    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04317    if (status != DB_SUCCESS)
04318       return DB_NO_KEY;
04319 
04320    return_status = CM_NO_CLIENT;
04321 
04322    /* loop over all clients */
04323    for (i = 0;; i++) {
04324       status = db_enum_key(hDB, hKey, i, &hSubkey);
04325       if (status == DB_NO_MORE_SUBKEYS)
04326          break;
04327 
04328       /* don't shutdown ourselves */
04329       if (hSubkey == hKeyClient)
04330          continue;
04331 
04332       if (status == DB_SUCCESS) {
04333          db_get_key(hDB, hSubkey, &key);
04334 
04335          /* contact client */
04336          size = sizeof(client_name);
04337          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04338 
04339          if (!bUnique)
04340             client_name[strlen(name)] = 0;      /* strip number */
04341 
04342          /* check if individual client */
04343          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
04344             continue;
04345 
04346          size = sizeof(port);
04347          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
04348 
04349          size = sizeof(remote_host);
04350          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
04351 
04352          /* client found -> connect to its server port */
04353          status = rpc_client_connect(remote_host, port, client_name, &hConn);
04354          if (status != RPC_SUCCESS) {
04355             return_status = CM_NO_CLIENT;
04356             sprintf(str, "cannot connect to client %s on host %s, port %d",
04357                     client_name, remote_host, port);
04358             cm_msg(MERROR, "cm_shutdown", str);
04359          } else {
04360             /* call disconnect with shutdown=TRUE */
04361             rpc_client_disconnect(hConn, TRUE);
04362 
04363             /* wait until client has shut down */
04364             start_time = ss_millitime();
04365             do {
04366                ss_sleep(100);
04367                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
04368             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
04369 
04370             if (status == DB_SUCCESS) {
04371                cm_msg(MINFO, "cm_shutdown",
04372                       "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
04373                       client_name);
04374                return_status = CM_NO_CLIENT;
04375             } else {
04376                return_status = CM_SUCCESS;
04377                i--;
04378             }
04379          }
04380       }
04381    }
04382 
04383    return return_status;
04384 }
04385 
04386 /********************************************************************/
04387 /**
04388 Check if a MIDAS client exists in current experiment
04389 @param    name            Client name
04390 @param    bUnique         If true, look for the exact client name.
04391                           If false, look for namexxx where xxx is
04392                           a any number
04393 @return   CM_SUCCESS, CM_NO_CLIENT 
04394 */
04395 INT cm_exist(char *name, BOOL bUnique)
04396 {
04397    INT status, i, size;
04398    HNDLE hDB, hKeyClient, hKey, hSubkey;
04399    char client_name[NAME_LENGTH];
04400 
04401    if (rpc_is_remote())
04402       return rpc_call(RPC_CM_EXIST, name, bUnique);
04403 
04404    cm_get_experiment_database(&hDB, &hKeyClient);
04405 
04406    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04407    if (status != DB_SUCCESS)
04408       return DB_NO_KEY;
04409 
04410    /* loop over all clients */
04411    for (i = 0;; i++) {
04412       status = db_enum_key(hDB, hKey, i, &hSubkey);
04413       if (status == DB_NO_MORE_SUBKEYS)
04414          break;
04415 
04416       if (hSubkey == hKeyClient)
04417          continue;
04418 
04419       if (status == DB_SUCCESS) {
04420          /* get client name */
04421          size = sizeof(client_name);
04422          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04423 
04424          if (equal_ustring(client_name, name))
04425             return CM_SUCCESS;
04426 
04427          if (!bUnique) {
04428             client_name[strlen(name)] = 0;      /* strip number */
04429             if (equal_ustring(client_name, name))
04430                return CM_SUCCESS;
04431          }
04432       }
04433    }
04434 
04435    return CM_NO_CLIENT;
04436 }
04437 
04438 /********************************************************************/
04439 /**
04440 Remove hanging clients independent of their watchdog
04441            timeout.
04442 
04443 Since this function does not obey the client watchdog
04444 timeout, it should be only called to remove clients which
04445 have their watchdog checking turned off or which are
04446 known to be dead. The normal client removement is done
04447 via cm_watchdog().
04448 
04449 Currently (Sept. 02) there are two applications for that:
04450 -# The ODBEdit command "cleanup", which can be used to
04451 remove clients which have their watchdog checking off,
04452 like the analyzer started with the "-d" flag for a
04453 debugging session.
04454 -# The frontend init code to remove previous frontends.
04455 This can be helpful if a frontend dies. Normally,
04456 one would have to wait 60 sec. for a crashed frontend
04457 to be removed. Only then one can start again the
04458 frontend. Since the frontend init code contains a
04459 call to cm_cleanup(<frontend_name>), one can restart
04460 a frontend immediately.
04461 
04462 Added ignore_timeout on Nov.03. A logger might have an
04463 increased tiemout of up to 60 sec. because of tape
04464 operations. If ignore_timeout is FALSE, the logger is
04465 then not killed if its inactivity is less than 60 sec., 
04466 while in the previous implementation it was always
04467 killed after 2*WATCHDOG_INTERVAL.
04468 @param    client_name      Client name, if zero check all clients
04469 @param    ignore_timeout   If TRUE, ignore a possible increased
04470                            timeout defined by each client.
04471 @return   CM_SUCCESS
04472 */
04473 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
04474 {
04475    if (rpc_is_remote())
04476       return rpc_call(RPC_CM_CLEANUP, client_name);
04477 
04478 #ifdef LOCAL_ROUTINES
04479    {
04480       BUFFER_HEADER *pheader = NULL;
04481       BUFFER_CLIENT *pbclient, *pbctmp;
04482       DATABASE_HEADER *pdbheader;
04483       DATABASE_CLIENT *pdbclient;
04484       KEY *pkey;
04485       INT client_pid;
04486       INT i, j, k, status, nc;
04487       BOOL bDeleted;
04488       char str[256];
04489       DWORD interval;
04490 
04491       /* check buffers */
04492       for (i = 0; i < _buffer_entries; i++)
04493          if (_buffer[i].attached) {
04494             /* update the last_activity entry to show that we are alive */
04495             pheader = _buffer[i].buffer_header;
04496             pbclient = pheader->client;
04497             pbclient[_buffer[i].client_index].last_activity = ss_millitime();
04498 
04499             /* now check other clients */
04500             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04501                if (j != _buffer[i].client_index && pbclient->pid &&
04502                    (client_name[0] == 0
04503                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
04504                   if (ignore_timeout)
04505                      interval = 2 * WATCHDOG_INTERVAL;
04506                   else
04507                      interval = pbclient->watchdog_timeout;
04508 
04509                   /* If client process has no activity, clear its buffer entry. */
04510                   if (ss_millitime() - pbclient->last_activity > interval) {
04511                      bm_lock_buffer(i + 1);
04512                      str[0] = 0;
04513 
04514                      /* now make again the check with the buffer locked */
04515                      if (ss_millitime() - pbclient->last_activity > interval) {
04516                         sprintf(str,
04517                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
04518                                 pbclient->name, pheader->name,
04519                                 (ss_millitime() - pbclient->last_activity) / 1000.0,
04520                                 interval / 1000.0);
04521 
04522                         /* clear entry from client structure in buffer header */
04523                         memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04524 
04525                         /* calculate new max_client_index entry */
04526                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04527                            if (pheader->client[k].pid != 0)
04528                               break;
04529                         pheader->max_client_index = k + 1;
04530 
04531                         /* count new number of clients */
04532                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04533                            if (pheader->client[k].pid != 0)
04534                               nc++;
04535                         pheader->num_clients = nc;
04536 
04537                         /* check if anyone is wating and wake him up */
04538                         pbctmp = pheader->client;
04539 
04540                         for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04541                            if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04542                               ss_resume(pbctmp->port, "B  ");
04543 
04544                      }
04545 
04546                      bm_unlock_buffer(i + 1);
04547 
04548                      /* display info message after unlocking buffer */
04549                      if (str[0])
04550                         cm_msg(MINFO, "cm_cleanup", str);
04551 
04552                      /* go again through whole list */
04553                      j = 0;
04554                   }
04555                }
04556          }
04557 
04558       /* check online databases */
04559       for (i = 0; i < _database_entries; i++)
04560          if (_database[i].attached) {
04561             /* update the last_activity entry to show that we are alive */
04562             db_lock_database(i + 1);
04563 
04564             pdbheader = _database[i].database_header;
04565             pdbclient = pdbheader->client;
04566             pdbclient[_database[i].client_index].last_activity = ss_millitime();
04567 
04568             /* now check other clients */
04569             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04570                if (j != _database[i].client_index && pdbclient->pid &&
04571                    (client_name[0] == 0
04572                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
04573                   client_pid = pdbclient->tid;
04574                   if (ignore_timeout)
04575                      interval = 2 * WATCHDOG_INTERVAL;
04576                   else
04577                      interval = pdbclient->watchdog_timeout;
04578 
04579                   /* If client process has no activity, clear its buffer entry. */
04580 
04581                   if (ss_millitime() - pdbclient->last_activity > interval) {
04582                      bDeleted = FALSE;
04583                      str[0] = 0;
04584 
04585                      /* now make again the check with the buffer locked */
04586                      if (ss_millitime() - pdbclient->last_activity > interval) {
04587                         sprintf(str,
04588                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
04589                                 pdbclient->name, pdbheader->name,
04590                                 (ss_millitime() - pdbclient->last_activity) / 1000.0,
04591                                 interval / 1000.0);
04592 
04593                         /* decrement notify_count for open records and clear exclusive mode */
04594                         for (k = 0; k < pdbclient->max_index; k++)
04595                            if (pdbclient->open_record[k].handle) {
04596                               pkey = (KEY *) ((char *) pdbheader +
04597                                               pdbclient->open_record[k].handle);
04598                               if (pkey->notify_count > 0)
04599                                  pkey->notify_count--;
04600 
04601                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04602                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
04603                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE),
04604                                              2);
04605                            }
04606 
04607                         /* clear entry from client structure in buffer header */
04608                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04609 
04610                         /* calculate new max_client_index entry */
04611                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04612                            if (pdbheader->client[k].pid != 0)
04613                               break;
04614                         pdbheader->max_client_index = k + 1;
04615 
04616                         /* count new number of clients */
04617                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04618                            if (pheader->client[k].pid != 0)
04619                               nc++;
04620                         pdbheader->num_clients = nc;
04621 
04622                         bDeleted = TRUE;
04623                      }
04624 
04625 
04626                      /* delete client entry after unlocking db */
04627                      if (bDeleted) {
04628                         db_unlock_database(i + 1);
04629 
04630                         /* display info message after unlocking buffer */
04631                         cm_msg(MINFO, "cm_cleanup", str);
04632 
04633                         status = cm_delete_client_info(i + 1, client_pid);
04634                         if (status != CM_SUCCESS)
04635                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
04636 
04637                         /* re-lock database */
04638                         db_lock_database(i + 1);
04639                         pdbheader = _database[i].database_header;
04640                         pdbclient = pdbheader->client;
04641 
04642                         /* go again though whole list */
04643                         j = 0;
04644                      }
04645                   }
04646                }
04647 
04648             db_unlock_database(i + 1);
04649          }
04650 
04651    }
04652 #endif                          /* LOCAL_ROUTINES */
04653 
04654    return CM_SUCCESS;
04655 }
04656 
04657 /**dox***************************************************************/
04658 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04659 
04660 /********************************************************************/
04661 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
04662 /********************************************************************\
04663 
04664   Routine: bm_buffer_info
04665 
04666   Purpose: Copies the current buffer header referenced by buffer_handle
04667            into the *buffer_header structure which must be supplied
04668            by the calling routine.
04669 
04670   Input:
04671     INT buffer_handle       Handle of the buffer to get the header from
04672 
04673   Output:
04674     BUFFER_HEADER *buffer_header   Destination address which gets a copy
04675                                    of the buffer header structure.
04676 
04677   Function value:
04678     BM_SUCCESS              Successful completion
04679     BM_INVALID_HANDLE       Buffer handle is invalid
04680     RPC_NET_ERROR           Network error
04681 
04682 \********************************************************************/
04683 {
04684    if (rpc_is_remote())
04685       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
04686 
04687 #ifdef LOCAL_ROUTINES
04688 
04689    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04690       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04691       return BM_INVALID_HANDLE;
04692    }
04693 
04694    if (!_buffer[buffer_handle - 1].attached) {
04695       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04696       return BM_INVALID_HANDLE;
04697    }
04698 
04699    bm_lock_buffer(buffer_handle);
04700 
04701    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
04702 
04703    bm_unlock_buffer(buffer_handle);
04704 
04705 #endif                          /* LOCAL_ROUTINES */
04706 
04707    return BM_SUCCESS;
04708 }
04709 
04710 /********************************************************************/
04711 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
04712 /********************************************************************\
04713 
04714   Routine: bm_get_buffer_level
04715 
04716   Purpose: Return number of bytes in buffer or in cache
04717 
04718   Input:
04719     INT buffer_handle       Handle of the buffer to get the info
04720 
04721   Output:
04722     INT *n_bytes              Number of bytes in buffer
04723 
04724   Function value:
04725     BM_SUCCESS              Successful completion
04726     BM_INVALID_HANDLE       Buffer handle is invalid
04727     RPC_NET_ERROR           Network error
04728 
04729 \********************************************************************/
04730 {
04731    if (rpc_is_remote())
04732       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
04733 
04734 #ifdef LOCAL_ROUTINES
04735    {
04736       BUFFER *pbuf;
04737       BUFFER_HEADER *pheader;
04738       BUFFER_CLIENT *pclient;
04739 
04740       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04741          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04742          return BM_INVALID_HANDLE;
04743       }
04744 
04745       pbuf = &_buffer[buffer_handle - 1];
04746       pheader = pbuf->buffer_header;
04747 
04748       if (!pbuf->attached) {
04749          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04750          return BM_INVALID_HANDLE;
04751       }
04752 
04753       bm_lock_buffer(buffer_handle);
04754 
04755       pclient = &(pheader->client[_buffer[buffer_handle - 1].client_index]);
04756 
04757       *n_bytes = pheader->write_pointer - pclient->read_pointer;
04758       if (*n_bytes < 0)
04759          *n_bytes += pheader->size;
04760 
04761       bm_unlock_buffer(buffer_handle);
04762 
04763       /* add bytes in cache */
04764       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
04765          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
04766    }
04767 #endif                          /* LOCAL_ROUTINES */
04768 
04769    return BM_SUCCESS;
04770 }
04771 
04772 
04773 
04774 #ifdef LOCAL_ROUTINES
04775 
04776 /********************************************************************/
04777 INT bm_lock_buffer(INT buffer_handle)
04778 /********************************************************************\
04779 
04780   Routine: bm_lock_buffer
04781 
04782   Purpose: Lock a buffer for exclusive access via system mutex calls.
04783 
04784   Input:
04785     INT    bufer_handle     Handle to the buffer to lock
04786   Output:
04787     none
04788 
04789   Function value:
04790     BM_SUCCESS              Successful completion
04791     BM_INVALID_HANDLE       Buffer handle is invalid
04792 
04793 \********************************************************************/
04794 {
04795    int status;
04796 
04797    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04798       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
04799       return BM_INVALID_HANDLE;
04800    }
04801 
04802    status = ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 5 * 60 * 1000);
04803 
04804    if (status != SS_SUCCESS) {
04805       cm_msg(MERROR, "bm_lock_buffer",
04806              "Cannot lock buffer handle %d, ss_mutex_wait_for() status %d", buffer_handle,
04807              status);
04808       abort();
04809       return BM_INVALID_HANDLE;
04810    }
04811 
04812    return BM_SUCCESS;
04813 }
04814 
04815 /********************************************************************/
04816 INT bm_unlock_buffer(INT buffer_handle)
04817 /********************************************************************\
04818 
04819   Routine: bm_unlock_buffer
04820 
04821   Purpose: Unlock a buffer via system mutex calls.
04822 
04823   Input:
04824     INT    bufer_handle     Handle to the buffer to lock
04825   Output:
04826     none
04827 
04828   Function value:
04829     BM_SUCCESS              Successful completion
04830     BM_INVALID_HANDLE       Buffer handle is invalid
04831 
04832 \********************************************************************/
04833 {
04834    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04835       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
04836       return BM_INVALID_HANDLE;
04837    }
04838 
04839    ss_mutex_release(_buffer[buffer_handle - 1].mutex);
04840    return BM_SUCCESS;
04841 }
04842 
04843 #endif                          /* LOCAL_ROUTINES */
04844 
04845 /********************************************************************/
04846 INT bm_init_buffer_counters(INT buffer_handle)
04847 /********************************************************************\
04848 
04849   Routine: bm_init_event_counters
04850 
04851   Purpose: Initialize counters for a specific buffer. This routine
04852            should be called at the beginning of a run.
04853 
04854   Input:
04855     INT    buffer_handle    Handle to the buffer to be
04856                             initialized.
04857   Output:
04858     none
04859 
04860   Function value:
04861     BM_SUCCESS              Successful completion
04862     BM_INVALID_HANDLE       Buffer handle is invalid
04863 
04864 \********************************************************************/
04865 {
04866    if (rpc_is_remote())
04867       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
04868 
04869 #ifdef LOCAL_ROUTINES
04870 
04871    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04872       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
04873              buffer_handle);
04874       return BM_INVALID_HANDLE;
04875    }
04876 
04877    if (!_buffer[buffer_handle - 1].attached) {
04878       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
04879              buffer_handle);
04880       return BM_INVALID_HANDLE;
04881    }
04882 
04883    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
04884    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
04885 
04886 #endif                          /* LOCAL_ROUTINES */
04887 
04888    return BM_SUCCESS;
04889 }
04890 
04891 /**dox***************************************************************/
04892 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04893 
04894 /**dox***************************************************************/
04895                    /** @} *//* end of cmfunctionc */
04896 
04897 /**dox***************************************************************/
04898 /** @addtogroup bmfunctionc
04899  *  
04900  *  @{  */
04901 
04902 /********************************************************************/
04903 /**
04904 Modifies buffer cache size.
04905 Without a buffer cache, events are copied to/from the shared
04906 memory event by event.
04907 
04908 To protect processed from accessing the shared memory simultaneously,
04909 semaphores are used. Since semaphore operations are CPU consuming (typically
04910 50-100us) this can slow down the data transfer especially for small events.
04911 By using a cache the number of semaphore operations is reduced dramatically.
04912 Instead writing directly to the shared memory, the events are copied to a
04913 local cache buffer. When this buffer is full, it is copied to the shared
04914 memory in one operation. The same technique can be used when receiving events.
04915 
04916 The drawback of this method is that the events have to be copied twice, once to the
04917 cache and once from the cache to the shared memory. Therefore it can happen that the
04918 usage of a cache even slows down data throughput on a given environment (computer
04919 type, OS type, event size).
04920 The cache size has therefore be optimized manually to maximize data throughput.
04921 @param buffer_handle buffer handle obtained via bm_open_buffer()
04922 @param read_size cache size for reading events in bytes, zero for no cache
04923 @param write_size cache size for writing events in bytes, zero for no cache
04924 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
04925 */
04926 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
04927 /*------------------------------------------------------------------*/
04928 {
04929    if (rpc_is_remote())
04930       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
04931 
04932 #ifdef LOCAL_ROUTINES
04933    {
04934       BUFFER *pbuf;
04935 
04936       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04937          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
04938          return BM_INVALID_HANDLE;
04939       }
04940 
04941       if (!_buffer[buffer_handle - 1].attached) {
04942          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
04943          return BM_INVALID_HANDLE;
04944       }
04945 
04946       if (read_size < 0 || read_size > 1E6) {
04947          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
04948          return BM_INVALID_PARAM;
04949       }
04950 
04951       if (write_size < 0 || write_size > 1E6) {
04952          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
04953          return BM_INVALID_PARAM;
04954       }
04955 
04956       /* manage read cache */
04957       pbuf = &_buffer[buffer_handle - 1];
04958 
04959       if (pbuf->read_cache_size > 0)
04960          M_FREE(pbuf->read_cache);
04961 
04962       if (read_size > 0) {
04963          pbuf->read_cache = (char *) M_MALLOC(read_size);
04964          if (pbuf->read_cache == NULL) {
04965             cm_msg(MERROR, "bm_set_cache_size",
04966                    "not enough memory to allocate cache buffer");
04967             return BM_NO_MEMORY;
04968          }
04969       }
04970 
04971       pbuf->read_cache_size = read_size;
04972       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
04973 
04974       /* manage write cache */
04975       if (pbuf->write_cache_size > 0)
04976          M_FREE(pbuf->write_cache);
04977 
04978       if (write_size > 0) {
04979          pbuf->write_cache = (char *) M_MALLOC(write_size);
04980          if (pbuf->write_cache == NULL) {
04981             cm_msg(MERROR, "bm_set_cache_size",
04982                    "not enough memory to allocate cache buffer");
04983             return BM_NO_MEMORY;
04984          }
04985       }
04986 
04987       pbuf->write_cache_size = write_size;
04988       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
04989 
04990    }
04991 #endif                          /* LOCAL_ROUTINES */
04992 
04993    return BM_SUCCESS;
04994 }
04995 
04996 /********************************************************************/
04997 /**
04998 Compose a Midas event header.
04999 An event header can usually be set-up manually or
05000 through this routine. If the data size of the event is not known when
05001 the header is composed, it can be set later with event_header->data-size = <...>
05002 Following structure is created at the beginning of an event
05003 \code
05004 typedef struct {
05005  short int     event_id;
05006  short int     trigger_mask;
05007  DWORD         serial_number;
05008  DWORD         time_stamp;
05009  DWORD         data_size;
05010 } EVENT_HEADER;
05011 
05012 char event[1000];
05013  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05014  *(event+sizeof(EVENT_HEADER)) = <...>
05015 \endcode
05016 @param event_header pointer to the event header
05017 @param event_id event ID of the event
05018 @param trigger_mask trigger mask of the event
05019 @param size size if the data part of the event in bytes
05020 @param serial serial number
05021 @return BM_SUCCESS
05022 */
05023 INT bm_compose_event(EVENT_HEADER * event_header,
05024                      short int event_id, short int trigger_mask, DWORD size, DWORD serial)
05025 {
05026    event_header->event_id = event_id;
05027    event_header->trigger_mask = trigger_mask;
05028    event_header->data_size = size;
05029    event_header->time_stamp = ss_time();
05030    event_header->serial_number = serial;
05031 
05032    return BM_SUCCESS;
05033 }
05034 
05035 
05036 /**dox***************************************************************/
05037 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05038 
05039 /********************************************************************/
05040 INT bm_add_event_request(INT buffer_handle, short int event_id,
05041                          short int trigger_mask,
05042                          INT sampling_type,
05043                          void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *),
05044                          INT request_id)
05045 /********************************************************************\
05046 
05047   Routine:  bm_add_event_request
05048 
05049   Purpose:  Place a request for a specific event type in the client
05050             structure of the buffer refereced by buffer_handle.
05051 
05052   Input:
05053     INT          buffer_handle  Handle to the buffer where the re-
05054                                 quest should be placed in
05055 
05056     short int    event_id       Event ID      \
05057     short int    trigger_mask   Trigger mask  / Event specification
05058 
05059     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05060 
05061 
05062                  Note: to request all types of events, use
05063                    event_id = 0 (all others should be !=0 !)
05064                    trigger_mask = TRIGGER_ALL
05065                    sampling_typ = GET_ALL
05066 
05067 
05068     void         *func          Callback function
05069     INT          request_id     Request id (unique number assigned
05070                                 by bm_request_event)
05071 
05072   Output:
05073     none
05074 
05075   Function value:
05076     BM_SUCCESS              Successful completion
05077     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05078                             MIDAS.H should be increased.
05079     BM_INVALID_HANDLE       Buffer handle is invalid
05080     RPC_NET_ERROR           Network error
05081 
05082 \********************************************************************/
05083 {
05084    if (rpc_is_remote())
05085       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05086                       trigger_mask, sampling_type, (INT) func, request_id);
05087 
05088 #ifdef LOCAL_ROUTINES
05089    {
05090       INT i;
05091       BUFFER_CLIENT *pclient;
05092 
05093       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05094          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05095                 buffer_handle);
05096          return BM_INVALID_HANDLE;
05097       }
05098 
05099       if (!_buffer[buffer_handle - 1].attached) {
05100          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05101                 buffer_handle);
05102          return BM_INVALID_HANDLE;
05103       }
05104 
05105       /* avoid callback/non callback requests */
05106       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05107          cm_msg(MERROR, "bm_add_event_request",
05108                 "mixing callback/non callback requests not possible");
05109          return BM_INVALID_MIXING;
05110       }
05111 
05112       /* get a pointer to the proper client structure */
05113       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05114                   client[_buffer[buffer_handle - 1].client_index]);
05115 
05116       /* lock buffer */
05117       bm_lock_buffer(buffer_handle);
05118 
05119       /* look for a empty request entry */
05120       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05121          if (!pclient->event_request[i].valid)
05122             break;
05123 
05124       if (i == MAX_EVENT_REQUESTS) {
05125          bm_unlock_buffer(buffer_handle);
05126          return BM_NO_MEMORY;
05127       }
05128 
05129       /* setup event_request structure */
05130       pclient->event_request[i].id = request_id;
05131       pclient->event_request[i].valid = TRUE;
05132       pclient->event_request[i].event_id = event_id;
05133       pclient->event_request[i].trigger_mask = trigger_mask;
05134       pclient->event_request[i].sampling_type = sampling_type;
05135       pclient->event_request[i].dispatch = func;
05136 
05137       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05138 
05139       /* set callback flag in buffer structure */
05140       if (func != NULL)
05141          _buffer[buffer_handle - 1].callback = TRUE;
05142 
05143       /*
05144          Save the index of the last request in the list so that later only the
05145          requests 0..max_request_index-1 have to be searched through.
05146        */
05147 
05148       if (i + 1 > pclient->max_request_index)
05149          pclient->max_request_index = i + 1;
05150 
05151       bm_unlock_buffer(buffer_handle);
05152    }
05153 #endif                          /* LOCAL_ROUTINES */
05154 
05155    return BM_SUCCESS;
05156 }
05157 
05158 /**dox***************************************************************/
05159 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05160 
05161 /********************************************************************/
05162 /**
05163 Place an event request based on certain characteristics.
05164 Multiple event requests can be placed for each buffer, which
05165 are later identified by their request ID. They can contain different callback
05166 routines. Example see bm_open_buffer() and bm_receive_event()
05167 @param buffer_handle buffer handle obtained via bm_open_buffer()
05168 @param event_id event ID for requested events. Use EVENTID_ALL
05169 to receive events with any ID.
05170 @param trigger_mask trigger mask for requested events.
05171 The requested events must have at least one bit in its
05172 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05173 receive events with any trigger mask.
05174 @param sampling_type specifies how many events to receive.
05175 A value of GET_ALL receives all events which
05176 match the specified event ID and trigger mask. If the events are consumed slower
05177 than produced, the producer is automatically slowed down. A value of GET_SOME
05178 receives as much events as possible without slowing down the producer. GET_ALL is
05179 typically used by the logger, while GET_SOME is typically used by analyzers.
05180 @param request_id request ID returned by the function.
05181 This ID is passed to the callback routine and must
05182 be used in the bm_delete_request() routine.
05183 @param func allback routine which gets called when an event of the
05184 specified type is received.
05185 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05186 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05187 should be increased.
05188 */
05189 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05190                      short int trigger_mask,
05191                      INT sampling_type, HNDLE * request_id,
05192                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05193 {
05194    INT index, status;
05195 
05196    /* allocate new space for the local request list */
05197    if (_request_list_entries == 0) {
05198       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05199       memset(_request_list, 0, sizeof(REQUEST_LIST));
05200       if (_request_list == NULL) {
05201          cm_msg(MERROR, "bm_request_event",
05202                 "not enough memory to allocate request list buffer");
05203          return BM_NO_MEMORY;
05204       }
05205 
05206       _request_list_entries = 1;
05207       index = 0;
05208    } else {
05209       /* check for a deleted entry */
05210       for (index = 0; index < _request_list_entries; index++)
05211          if (!_request_list[index].buffer_handle)
05212             break;
05213 
05214       /* if not found, create new one */
05215       if (index == _request_list_entries) {
05216          _request_list = (REQUEST_LIST *) realloc(_request_list,
05217                                                   sizeof(REQUEST_LIST) *
05218                                                   (_request_list_entries + 1));
05219          if (_request_list == NULL) {
05220             cm_msg(MERROR, "bm_request_event",
05221                    "not enough memory to allocate request list buffer");
05222             return BM_NO_MEMORY;
05223          }
05224 
05225          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05226 
05227          _request_list_entries++;
05228       }
05229    }
05230 
05231    /* initialize request list */
05232    _request_list[index].buffer_handle = buffer_handle;
05233    _request_list[index].event_id = event_id;
05234    _request_list[index].trigger_mask = trigger_mask;
05235    _request_list[index].dispatcher = func;
05236 
05237    *request_id = index;
05238 
05239    /* add request in buffer structure */
05240    status = bm_add_event_request(buffer_handle, event_id, trigger_mask,
05241                                  sampling_type, func, index);
05242    if (status != BM_SUCCESS)
05243       return status;
05244 
05245    return BM_SUCCESS;
05246 }
05247 
05248 /********************************************************************/
05249 /**
05250 Delete a previously placed request for a specific event
05251 type in the client structure of the buffer refereced by buffer_handle.
05252 @param buffer_handle  Handle to the buffer where the re-
05253                                 quest should be placed in
05254 @param request_id     Request id returned by bm_request_event
05255 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR 
05256 */
05257 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05258 {
05259    if (rpc_is_remote())
05260       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
05261 
05262 #ifdef LOCAL_ROUTINES
05263    {
05264       INT i, deleted;
05265       BUFFER_CLIENT *pclient;
05266 
05267       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05268          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05269                 buffer_handle);
05270          return BM_INVALID_HANDLE;
05271       }
05272 
05273       if (!_buffer[buffer_handle - 1].attached) {
05274          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05275                 buffer_handle);
05276          return BM_INVALID_HANDLE;
05277       }
05278 
05279       /* get a pointer to the proper client structure */
05280       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05281                   client[_buffer[buffer_handle - 1].client_index]);
05282 
05283       /* lock buffer */
05284       bm_lock_buffer(buffer_handle);
05285 
05286       /* check all requests and set to zero if matching */
05287       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
05288          if (pclient->event_request[i].valid &&
05289              pclient->event_request[i].id == request_id) {
05290             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
05291             deleted++;
05292          }
05293 
05294       /* calculate new max_request_index entry */
05295       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
05296          if (pclient->event_request[i].valid)
05297             break;
05298 
05299       pclient->max_request_index = i + 1;
05300 
05301       /* caluclate new all_flag */
05302       pclient->all_flag = FALSE;
05303 
05304       for (i = 0; i < pclient->max_request_index; i++)
05305          if (pclient->event_request[i].valid &&
05306              (pclient->event_request[i].sampling_type & GET_ALL)) {
05307             pclient->all_flag = TRUE;
05308             break;
05309          }
05310 
05311       bm_unlock_buffer(buffer_handle);
05312 
05313       if (!deleted)
05314          return BM_NOT_FOUND;
05315    }
05316 #endif                          /* LOCAL_ROUTINES */
05317 
05318    return BM_SUCCESS;
05319 }
05320 
05321 /********************************************************************/
05322 /** 
05323 Deletes an event request previously done with bm_request_event().
05324 When an event request gets deleted, events of that requested type are
05325 not received any more. When a buffer is closed via bm_close_buffer(), all
05326 event requests from that buffer are deleted automatically
05327 @param request_id request identifier given by bm_request_event()
05328 @return BM_SUCCESS, BM_INVALID_HANDLE
05329 */
05330 INT bm_delete_request(INT request_id)
05331 {
05332    if (request_id < 0 || request_id >= _request_list_entries)
05333       return BM_INVALID_HANDLE;
05334 
05335    /* remove request entry from buffer */
05336    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
05337 
05338    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
05339 
05340    return BM_SUCCESS;
05341 }
05342 
05343 /********************************************************************/
05344 /** 
05345 Sends an event to a buffer.
05346 This function check if the buffer has enough space for the
05347 event, then copies the event to the buffer in shared memory.
05348 If clients have requests for the event, they are notified via an UDP packet.
05349 \code
05350 char event[1000];
05351 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05352 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05353 
05354 // set first byte of event
05355 *(event+sizeof(EVENT_HEADER)) = <...>
05356 #include <stdio.h>
05357 #include "midas.h"
05358 main()
05359 {
05360  INT status, i;
05361  HNDLE hbuf;
05362  char event[1000];
05363  status = cm_connect_experiment("", "Sample", "Producer", NULL);
05364  if (status != CM_SUCCESS)
05365  return 1;
05366  bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
05367 
05368  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05369  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05370 
05371  // set event data
05372  for (i=0 ; i<100 ; i++)
05373  *(event+sizeof(EVENT_HEADER)+i) = i;
05374  // send event
05375  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
05376  cm_disconnect_experiment();
05377  return 0;
05378 }
05379 \endcode
05380 @param buffer_handle Buffer handle obtained via bm_open_buffer()
05381 @param source Address of event buffer
05382 @param buf_size Size of event including event header in bytes
05383 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
05384 blocks if the buffer has not enough free space to receive the event.
05385 If TRUE, the function returns immediately with a
05386 value of BM_ASYNC_RETURN without writing the event to the buffer
05387 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
05388 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
05389 buffer has not enough space to receive event<br>
05390 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
05391 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h and
05392 recompile.
05393 */
05394 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
05395 {
05396    EVENT_HEADER *pevent;
05397 
05398    /* check if event size defined in header matches buf_size */
05399    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
05400                                         sizeof(EVENT_HEADER))) {
05401       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
05402              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
05403                                             sizeof(EVENT_HEADER)));
05404       return BM_INVALID_PARAM;
05405    }
05406 
05407    /* check for maximal event size */
05408    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
05409       cm_msg(MERROR, "bm_send_event",
05410              "event size (%d) larger than maximum event size (%d)",
05411              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
05412       return BM_NO_MEMORY;
05413    }
05414 
05415    if (rpc_is_remote())
05416       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
05417 
05418 #ifdef LOCAL_ROUTINES
05419    {
05420       BUFFER *pbuf;
05421       BUFFER_HEADER *pheader;
05422       BUFFER_CLIENT *pclient, *pc;
05423       EVENT_REQUEST *prequest;
05424       EVENT_HEADER *pevent_test;
05425       INT i, j, min_wp, size, total_size, status;
05426       INT increment;
05427       INT my_client_index;
05428       INT old_write_pointer;
05429       INT old_read_pointer, new_read_pointer;
05430       INT num_requests_client;
05431       char *pdata;
05432       BOOL blocking;
05433       INT n_blocking;
05434       INT request_id;
05435       char str[80];
05436 
05437       pbuf = &_buffer[buffer_handle - 1];
05438 
05439       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05440          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
05441          return BM_INVALID_HANDLE;
05442       }
05443 
05444       if (!pbuf->attached) {
05445          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
05446          return BM_INVALID_HANDLE;
05447       }
05448 
05449       pevent = (EVENT_HEADER *) source;
05450       total_size = buf_size;
05451 
05452       /* round up total_size to next DWORD boundary */
05453       total_size = ALIGN8(total_size);
05454 
05455       /* look if there is space in the cache */
05456       if (pbuf->write_cache_size) {
05457          status = BM_SUCCESS;
05458 
05459          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
05460             status = bm_flush_cache(buffer_handle, async_flag);
05461 
05462          if (status != BM_SUCCESS)
05463             return status;
05464 
05465          if (total_size < pbuf->write_cache_size) {
05466             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
05467 
05468             pbuf->write_cache_wp += total_size;
05469             return BM_SUCCESS;
05470          }
05471       }
05472 
05473       /* calculate some shorthands */
05474       pheader = _buffer[buffer_handle - 1].buffer_header;
05475       pdata = (char *) (pheader + 1);
05476       my_client_index = _buffer[buffer_handle - 1].client_index;
05477       pclient = pheader->client;
05478 
05479       /* check if buffer is large enough */
05480       if (total_size >= pheader->size) {
05481          cm_msg(MERROR, "bm_send_event",
05482                 "total event size (%d) larger than buffer size (%d)", total_size,
05483                 pheader->size);
05484          return BM_NO_MEMORY;
05485       }
05486 
05487       /* lock the buffer */
05488       bm_lock_buffer(buffer_handle);
05489 
05490       /* check if enough space in buffer left */
05491       do {
05492          size = pheader->read_pointer - pheader->write_pointer;
05493          if (size <= 0)
05494             size += pheader->size;
05495 
05496          if (size <= total_size) {      /* note the '<=' to avoid 100% filling */
05497             /* if not enough space, find out who's blocking */
05498             n_blocking = 0;
05499 
05500             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05501                if (pc->pid) {
05502                   if (pc->read_pointer == pheader->read_pointer) {
05503                      /*
05504                         First assume that the client with the "minimum" read pointer
05505                         is not really blocking due to a GET_ALL request.
05506                       */
05507                      blocking = FALSE;
05508                      request_id = -1;
05509 
05510                      /* check if this request blocks */
05511 
05512                      prequest = pc->event_request;
05513                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
05514 
05515                      for (j = 0; j < pc->max_request_index; j++, prequest++)
05516                         if (prequest->valid &&
05517                             bm_match_event(prequest->event_id, prequest->trigger_mask,
05518                                            pevent_test)) {
05519                            request_id = prequest->id;
05520                            if (prequest->sampling_type & GET_ALL) {
05521                               blocking = TRUE;
05522                               break;
05523                            }
05524                         }
05525 
05526                      if (!blocking) {
05527                         /*
05528                            The blocking guy has no GET_ALL request for this event
05529                            -> shift its read pointer.
05530                          */
05531 
05532                         old_read_pointer = pc->read_pointer;
05533 
05534                         increment = sizeof(EVENT_HEADER) +
05535                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
05536 
05537                         /* correct increment for DWORD boundary */
05538                         increment = ALIGN8(increment);
05539 
05540                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
05541 
05542                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05543                            new_read_pointer = 0;
05544 
05545                         pc->read_pointer = new_read_pointer;
05546                      } else {
05547                         n_blocking++;
05548                      }
05549 
05550                      /* wake that client if it has a request */
05551                      if (pc->read_wait && request_id != -1) {
05552 #ifdef DEBUG_MSG
05553                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
05554                                pheader->read_pointer, pheader->write_pointer);
05555 #endif
05556                         sprintf(str, "B %s %d", pheader->name, request_id);
05557                         ss_resume(pc->port, str);
05558                      }
05559 
05560 
05561                   }             /* read_pointer blocks */
05562                }
05563             /* client loop */
05564             if (n_blocking > 0) {
05565                /* at least one client is blocking */
05566 
05567                bm_unlock_buffer(buffer_handle);
05568 
05569                /* return now in ASYNC mode */
05570                if (async_flag)
05571                   return BM_ASYNC_RETURN;
05572 
05573 #ifdef DEBUG_MSG
05574                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
05575                       pheader->read_pointer,
05576                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05577 #endif
05578 
05579                /* has the read pointer moved in between ? */
05580                size = pheader->read_pointer - pheader->write_pointer;
05581                if (size <= 0)
05582                   size += pheader->size;
05583 
05584                /* suspend process */
05585                if (size <= total_size) {
05586                   /* signal other clients wait mode */
05587                   pclient[my_client_index].write_wait = total_size;
05588 
05589                   status = ss_suspend(1000, MSG_BM);
05590 
05591                   pclient[my_client_index].write_wait = 0;
05592 
05593                   /* return if TCP connection broken */
05594                   if (status == SS_ABORT)
05595                      return SS_ABORT;
05596                }
05597 #ifdef DEBUG_MSG
05598                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
05599                       pheader->read_pointer,
05600                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05601 #endif
05602 
05603                bm_lock_buffer(buffer_handle);
05604 
05605                /* has the write pointer moved in between ? */
05606                size = pheader->read_pointer - pheader->write_pointer;
05607                if (size <= 0)
05608                   size += pheader->size;
05609             } else {
05610                /*
05611                   calculate new global read pointer as "minimum" of
05612                   client read pointers
05613                 */
05614                min_wp = pheader->write_pointer;
05615 
05616                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05617                   if (pc->pid) {
05618                      if (pc->read_pointer < min_wp)
05619                         min_wp = pc->read_pointer;
05620 
05621                      if (pc->read_pointer > pheader->write_pointer &&
05622                          pc->read_pointer - pheader->size < min_wp)
05623                         min_wp = pc->read_pointer - pheader->size;
05624                   }
05625 
05626                if (min_wp < 0)
05627                   min_wp += pheader->size;
05628 
05629                pheader->read_pointer = min_wp;
05630             }
05631 
05632          }
05633          /* if (size <= total_size) */
05634       } while (size <= total_size);
05635 
05636       /* we have space, so let's copy the event */
05637       old_write_pointer = pheader->write_pointer;
05638 
05639       if (pheader->write_pointer + total_size <= pheader->size) {
05640          memcpy(pdata + pheader->write_pointer, pevent, total_size);
05641          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
05642          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05643             pheader->write_pointer = 0;
05644       } else {
05645          /* split event */
05646          size = pheader->size - pheader->write_pointer;
05647 
05648          memcpy(pdata + pheader->write_pointer, pevent, size);
05649          memcpy(pdata, (char *) pevent + size, total_size - size);
05650 
05651          pheader->write_pointer = total_size - size;
05652       }
05653 
05654       /* check which clients have a request for this event */
05655       for (i = 0; i < pheader->max_client_index; i++)
05656          if (pclient[i].pid) {
05657             prequest = pclient[i].event_request;
05658             num_requests_client = 0;
05659             request_id = -1;
05660 
05661             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
05662                if (prequest->valid &&
05663                    bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
05664                   if (prequest->sampling_type & GET_ALL)
05665                      pclient[i].num_waiting_events++;
05666 
05667                   num_requests_client++;
05668                   request_id = prequest->id;
05669                }
05670 
05671             /* if that client has a request and is suspended, wake it up */
05672             if (num_requests_client && pclient[i].read_wait) {
05673 #ifdef DEBUG_MSG
05674                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
05675                       pheader->write_pointer);
05676 #endif
05677                sprintf(str, "B %s %d", pheader->name, request_id);
05678                ss_resume(pclient[i].port, str);
05679             }
05680 
05681             /* if that client has no request, shift its read pointer */
05682             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
05683                pclient[i].read_pointer = pheader->write_pointer;
05684          }
05685 
05686       /* shift read pointer of own client */
05687 /* 16.4.99 SR, outcommented to receive own messages
05688 
05689   if (pclient[my_client_index].read_pointer == old_write_pointer)
05690     pclient[my_client_index].read_pointer = pheader->write_pointer;
05691 */
05692 
05693       /* calculate global read pointer as "minimum" of client read pointers */
05694       min_wp = pheader->write_pointer;
05695 
05696       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05697          if (pc->pid) {
05698             if (pc->read_pointer < min_wp)
05699                min_wp = pc->read_pointer;
05700 
05701             if (pc->read_pointer > pheader->write_pointer &&
05702                 pc->read_pointer - pheader->size < min_wp)
05703                min_wp = pc->read_pointer - pheader->size;
05704          }
05705 
05706       if (min_wp < 0)
05707          min_wp += pheader->size;
05708 
05709 #ifdef DEBUG_MSG
05710       if (min_wp == pheader->read_pointer)
05711          cm_msg(MDEBUG, "bm_send_event -> wp=%d", pheader->write_pointer);
05712       else
05713          cm_msg(MDEBUG, "bm_send_event -> wp=%d, rp %d -> %d, size=%d",
05714                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
05715 #endif
05716 
05717       pheader->read_pointer = min_wp;
05718 
05719       /* update statistics */
05720       pheader->num_in_events++;
05721 
05722       /* unlock the buffer */
05723       bm_unlock_buffer(buffer_handle);
05724    }
05725 #endif                          /* LOCAL_ROUTINES */
05726 
05727    return BM_SUCCESS;
05728 }
05729 
05730 /********************************************************************/
05731 /**
05732 Empty write cache.
05733 This function should be used if events in the write cache
05734 should be visible to the consumers immediately. It should be called at the
05735 end of each run, otherwise events could be kept in the write buffer and will
05736 flow to the data of the next run.
05737 @param buffer_handle Buffer handle obtained via bm_open_buffer()
05738 @param async_flag Synchronous/asynchronous flag.
05739 If FALSE, the function blocks if the buffer has not
05740 enough free space to receive the full cache. If TRUE, the function returns
05741 immediately with a value of BM_ASYNC_RETURN without writing the cache.
05742 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
05743 BM_ASYNC_RETURN Routine called with async_flag == TRUE
05744 and buffer has not enough space to receive cache<br>
05745 BM_NO_MEMORY Event is too large for network buffer or event buffer.
05746 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h
05747 and recompile.
05748 */
05749 INT bm_flush_cache(INT buffer_handle, INT async_flag)
05750 {
05751    if (rpc_is_remote())
05752       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
05753 
05754 #ifdef LOCAL_ROUTINES
05755    {
05756       BUFFER *pbuf;
05757       BUFFER_HEADER *pheader;
05758       BUFFER_CLIENT *pclient, *pc;
05759       EVENT_REQUEST *prequest;
05760       EVENT_HEADER *pevent, *pevent_test;
05761       INT i, j, min_wp, size, total_size, status;
05762       INT increment;
05763       INT my_client_index;
05764       INT old_write_pointer;
05765       INT old_read_pointer, new_read_pointer;
05766       char *pdata;
05767       BOOL blocking;
05768       INT n_blocking;
05769       INT request_id;
05770       char str[80];
05771 
05772       pbuf = &_buffer[buffer_handle - 1];
05773 
05774       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05775          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
05776          return BM_INVALID_HANDLE;
05777       }
05778 
05779       if (!pbuf->attached) {
05780          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
05781          return BM_INVALID_HANDLE;
05782       }
05783 
05784       if (pbuf->write_cache_size == 0)
05785          return BM_SUCCESS;
05786 
05787       /* check if anything needs to be flushed */
05788       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
05789          return BM_SUCCESS;
05790 
05791       /* calculate some shorthands */
05792       pheader = _buffer[buffer_handle - 1].buffer_header;
05793       pdata = (char *) (pheader + 1);
05794       my_client_index = _buffer[buffer_handle - 1].client_index;
05795       pclient = pheader->client;
05796       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
05797 
05798       /* lock the buffer */
05799       bm_lock_buffer(buffer_handle);
05800 
05801 #ifdef DEBUG_MSG
05802       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer,
05803              pheader->write_pointer);
05804 #endif
05805 
05806       /* check if enough space in buffer left */
05807       do {
05808          size = pheader->read_pointer - pheader->write_pointer;
05809          if (size <= 0)
05810             size += pheader->size;
05811 
05812          if (size <= pbuf->write_cache_wp) {
05813             /* if not enough space, find out who's blocking */
05814             n_blocking = 0;
05815 
05816             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05817                if (pc->pid) {
05818                   if (pc->read_pointer == pheader->read_pointer) {
05819                      /*
05820                         First assume that the client with the "minimum" read pointer
05821                         is not really blocking due to a GET_ALL request.
05822                       */
05823                      blocking = FALSE;
05824                      request_id = -1;
05825 
05826                      /* check if this request blocks. */
05827                      prequest = pc->event_request;
05828                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
05829 
05830                      for (j = 0; j < pc->max_request_index; j++, prequest++)
05831                         if (prequest->valid &&
05832                             bm_match_event(prequest->event_id, prequest->trigger_mask,
05833                                            pevent_test)) {
05834                            request_id = prequest->id;
05835                            if (prequest->sampling_type & GET_ALL) {
05836                               blocking = TRUE;
05837                               break;
05838                            }
05839                         }
05840 
05841                      if (!blocking) {
05842                         /*
05843                            The blocking guy has no GET_ALL request for this event
05844                            -> shift its read pointer.
05845                          */
05846 
05847                         old_read_pointer = pc->read_pointer;
05848 
05849                         increment = sizeof(EVENT_HEADER) +
05850                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
05851 
05852                         /* correct increment for DWORD boundary */
05853                         increment = ALIGN8(increment);
05854 
05855                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
05856 
05857                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05858                            new_read_pointer = 0;
05859 
05860 #ifdef DEBUG_MSG
05861                         cm_msg(MDEBUG, "bm_flush_cache: shift client %d rp=%d -> =%d", i,
05862                                pc->read_pointer, new_read_pointer);
05863 #endif
05864 
05865                         pc->read_pointer = new_read_pointer;
05866                      } else {
05867                         n_blocking++;
05868                      }
05869 
05870                      /* wake that client if it has a request */
05871                      if (pc->read_wait && request_id != -1) {
05872 #ifdef DEBUG_MSG
05873                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
05874                                pheader->read_pointer, pheader->write_pointer);
05875 #endif
05876                         sprintf(str, "B %s %d", pheader->name, request_id);
05877                         ss_resume(pc->port, str);
05878                      }
05879 
05880 
05881                   }             /* read_pointer blocks */
05882                }
05883             /* client loop */
05884             if (n_blocking > 0) {
05885                /* at least one client is blocking */
05886 
05887                bm_unlock_buffer(buffer_handle);
05888 
05889                /* return now in ASYNC mode */
05890                if (async_flag)
05891                   return BM_ASYNC_RETURN;
05892 
05893 #ifdef DEBUG_MSG
05894                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
05895                       pheader->read_pointer,
05896                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05897 #endif
05898 
05899                /* has the read pointer moved in between ? */
05900                size = pheader->read_pointer - pheader->write_pointer;
05901                if (size <= 0)
05902                   size += pheader->size;
05903 
05904                /* suspend process */
05905                if (size <= pbuf->write_cache_wp) {
05906                   /* signal other clients wait mode */
05907                   pclient[my_client_index].write_wait = pbuf->write_cache_wp;
05908 
05909                   status = ss_suspend(1000, MSG_BM);
05910 
05911                   pclient[my_client_index].write_wait = 0;
05912 
05913                   /* return if TCP connection broken */
05914                   if (status == SS_ABORT)
05915                      return SS_ABORT;
05916                }
05917 #ifdef DEBUG_MSG
05918                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
05919                       pheader->read_pointer,
05920                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05921 #endif
05922 
05923                bm_lock_buffer(buffer_handle);
05924 
05925                /* has the write pointer moved in between ? */
05926                size = pheader->read_pointer - pheader->write_pointer;
05927                if (size <= 0)
05928                   size += pheader->size;
05929             } else {
05930                /*
05931                   calculate new global read pointer as "minimum" of
05932                   client read pointers
05933                 */
05934                min_wp = pheader->write_pointer;
05935 
05936                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05937                   if (pc->pid) {
05938                      if (pc->read_pointer < min_wp)
05939                         min_wp = pc->read_pointer;
05940 
05941                      if (pc->read_pointer > pheader->write_pointer &&
05942                          pc->read_pointer - pheader->size < min_wp)
05943                         min_wp = pc->read_pointer - pheader->size;
05944                   }
05945 
05946                if (min_wp < 0)
05947                   min_wp += pheader->size;
05948 
05949                pheader->read_pointer = min_wp;
05950             }
05951 
05952          }
05953          /* if (size <= total_size) */
05954       } while (size <= pbuf->write_cache_wp);
05955 
05956 
05957       /* we have space, so let's copy the event */
05958       old_write_pointer = pheader->write_pointer;
05959 
05960 #ifdef DEBUG_MSG
05961       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
05962              pheader->write_pointer);
05963 #endif
05964 
05965       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
05966          /* loop over all events in cache */
05967 
05968          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
05969          total_size = pevent->data_size + sizeof(EVENT_HEADER);
05970 
05971          /* correct size for DWORD boundary */
05972          total_size = ALIGN8(total_size);
05973 
05974          if (pheader->write_pointer + total_size <= pheader->size) {
05975             memcpy(pdata + pheader->write_pointer, pevent, total_size);
05976             pheader->write_pointer = (pheader->write_pointer + total_size) %
05977                 pheader->size;
05978             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05979                pheader->write_pointer = 0;
05980          } else {
05981             /* split event */
05982             size = pheader->size - pheader->write_pointer;
05983 
05984             memcpy(pdata + pheader->write_pointer, pevent, size);
05985             memcpy(pdata, (char *) pevent + size, total_size - size);
05986 
05987             pheader->write_pointer = total_size - size;
05988          }
05989 
05990          pbuf->write_cache_rp += total_size;
05991       }
05992 
05993       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05994 
05995       /* check which clients are waiting */
05996       for (i = 0; i < pheader->max_client_index; i++)
05997          if (pclient[i].pid && pclient[i].read_wait) {
05998 #ifdef DEBUG_MSG
05999             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06000                    pheader->write_pointer);
06001 #endif
06002             sprintf(str, "B %s %d", pheader->name, -1);
06003             ss_resume(pclient[i].port, str);
06004          }
06005 
06006       /* shift read pointer of own client */
06007 /* 16.4.99 SR, outcommented to receive own messages
06008 
06009   if (pclient[my_client_index].read_pointer == old_write_pointer)
06010     pclient[my_client_index].read_pointer = pheader->write_pointer;
06011 */
06012 
06013       /* calculate global read pointer as "minimum" of client read pointers */
06014       min_wp = pheader->write_pointer;
06015 
06016       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06017          if (pc->pid) {
06018 #ifdef DEBUG_MSG
06019             cm_msg(MDEBUG, "bm_flush_cache: client %d rp=%d", i, pc->read_pointer);
06020 #endif
06021             if (pc->read_pointer < min_wp)
06022                min_wp = pc->read_pointer;
06023 
06024             if (pc->read_pointer > pheader->write_pointer &&
06025                 pc->read_pointer - pheader->size < min_wp)
06026                min_wp = pc->read_pointer - pheader->size;
06027          }
06028 
06029       if (min_wp < 0)
06030          min_wp += pheader->size;
06031 
06032 #ifdef DEBUG_MSG
06033       if (min_wp == pheader->read_pointer)
06034          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d", pheader->write_pointer);
06035       else
06036          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d, rp %d -> %d, size=%d",
06037                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06038 #endif
06039 
06040       pheader->read_pointer = min_wp;
06041 
06042       /* update statistics */
06043       pheader->num_in_events++;
06044 
06045       /* unlock the buffer */
06046       bm_unlock_buffer(buffer_handle);
06047    }
06048 #endif                          /* LOCAL_ROUTINES */
06049 
06050    return BM_SUCCESS;
06051 }
06052 
06053 /********************************************************************/
06054 /**
06055 Receives events directly.
06056 This function is an alternative way to receive events without
06057 a main loop.
06058 
06059 It can be used in analysis systems which actively receive events,
06060 rather than using callbacks. A analysis package could for example contain its own
06061 command line interface. A command
06062 like "receive 1000 events" could make it necessary to call bm_receive_event()
06063 1000 times in a row to receive these events and then return back to the
06064 command line prompt.
06065 The according bm_request_event() call contains NULL as the
06066 callback routine to indicate that bm_receive_event() is called to receive
06067 events.
06068 \code
06069 #include <stdio.h>
06070 #include "midas.h"
06071 void process_event(EVENT_HEADER *pheader)
06072 {
06073  printf("Received event #%d\r",
06074  pheader->serial_number);
06075 }
06076 main()
06077 {
06078   INT status, request_id;
06079   HNDLE hbuf;
06080   char event_buffer[1000];
06081   status = cm_connect_experiment("", "Sample",
06082   "Simple Analyzer", NULL);
06083   if (status != CM_SUCCESS)
06084    return 1;
06085   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06086   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06087 
06088   do
06089   {
06090    size = sizeof(event_buffer);
06091    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06092   if (status == CM_SUCCESS)
06093    process_event((EVENT_HEADER *) event_buffer);
06094    <...do something else...>
06095    status = cm_yield(0);
06096   } while (status != RPC_SHUTDOWN &&
06097   status != SS_ABORT);
06098   cm_disconnect_experiment();
06099   return 0;
06100 }
06101 \endcode
06102 @param buffer_handle buffer handle
06103 @param destination destination address where event is written to
06104 @param buf_size size of destination buffer on input, size of event plus
06105 header on return.
06106 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06107 blocks if no event is available. If TRUE, the function returns immediately
06108 with a value of BM_ASYNC_RETURN without receiving any event.
06109 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06110 BM_TRUNCATED   The event is larger than the destination buffer and was
06111                therefore truncated <br>
06112 BM_ASYNC_RETURN No event available
06113 */
06114 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06115 {
06116    if (rpc_is_remote()) {
06117       if (*buf_size > NET_BUFFER_SIZE) {
06118          cm_msg(MERROR, "bm_receive_event",
06119                 "max. event size larger than NET_BUFFER_SIZE");
06120          return RPC_NET_ERROR;
06121       }
06122 
06123       return rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle,
06124                       destination, buf_size, async_flag);
06125    }
06126 #ifdef LOCAL_ROUTINES
06127    {
06128       BUFFER *pbuf;
06129       BUFFER_HEADER *pheader;
06130       BUFFER_CLIENT *pclient, *pc, *pctmp;
06131       EVENT_REQUEST *prequest;
06132       EVENT_HEADER *pevent;
06133       char *pdata;
06134       INT convert_flags;
06135       INT i, min_wp, size, max_size, total_size, status = 0;
06136       INT my_client_index;
06137       BOOL found;
06138       INT old_read_pointer, new_read_pointer;
06139 
06140       pbuf = &_buffer[buffer_handle - 1];
06141 
06142       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06143          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06144          return BM_INVALID_HANDLE;
06145       }
06146 
06147       if (!pbuf->attached) {
06148          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06149          return BM_INVALID_HANDLE;
06150       }
06151 
06152       max_size = *buf_size;
06153       *buf_size = 0;
06154 
06155       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06156          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06157       else
06158          convert_flags = 0;
06159 
06160     CACHE_READ:
06161 
06162       /* look if there is anything in the cache */
06163       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06164          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06165          size = pevent->data_size + sizeof(EVENT_HEADER);
06166 
06167          if (size > max_size) {
06168             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, max_size);
06169             cm_msg(MERROR, "bm_receive_event", "event size larger than buffer size");
06170             *buf_size = max_size;
06171             status = BM_TRUNCATED;
06172          } else {
06173             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, size);
06174             *buf_size = size;
06175             status = BM_SUCCESS;
06176          }
06177 
06178          /* now convert event header */
06179          if (convert_flags) {
06180             pevent = (EVENT_HEADER *) destination;
06181             rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
06182             rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06183                                convert_flags);
06184             rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06185                                convert_flags);
06186             rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06187                                convert_flags);
06188             rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06189                                convert_flags);
06190          }
06191 
06192          /* correct size for DWORD boundary */
06193          size = ALIGN8(size);
06194 
06195          pbuf->read_cache_rp += size;
06196 
06197          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06198             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06199 
06200          return status;
06201       }
06202 
06203       /* calculate some shorthands */
06204       pheader = pbuf->buffer_header;
06205       pdata = (char *) (pheader + 1);
06206       my_client_index = pbuf->client_index;
06207       pclient = pheader->client;
06208       pc = pheader->client + my_client_index;
06209 
06210       /* first do a quick check without locking the buffer */
06211       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06212          return BM_ASYNC_RETURN;
06213 
06214       /* lock the buffer */
06215       bm_lock_buffer(buffer_handle);
06216 
06217     LOOP:
06218 
06219       while (pheader->write_pointer == pc->read_pointer) {
06220          bm_unlock_buffer(buffer_handle);
06221 
06222          /* return now in ASYNC mode */
06223          if (async_flag == ASYNC)
06224             return BM_ASYNC_RETURN;
06225 
06226          pc->read_wait = TRUE;
06227 
06228          /* check again pointers (may have moved in between) */
06229          if (pheader->write_pointer == pc->read_pointer) {
06230 #ifdef DEBUG_MSG
06231             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06232                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06233 #endif
06234 
06235             status = ss_suspend(1000, MSG_BM);
06236 
06237 #ifdef DEBUG_MSG
06238             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d",
06239                    pheader->read_pointer, pheader->write_pointer);
06240 #endif
06241 
06242             /* return if TCP connection broken */
06243             if (status == SS_ABORT)
06244                return SS_ABORT;
06245          }
06246 
06247          pc->read_wait = FALSE;
06248 
06249          bm_lock_buffer(buffer_handle);
06250       }
06251 
06252       /* check if event at current read pointer matches a request */
06253       found = FALSE;
06254 
06255       do {
06256          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06257 
06258          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06259          total_size = ALIGN8(total_size);
06260 
06261          prequest = pc->event_request;
06262 
06263          for (i = 0; i < pc->max_request_index; i++, prequest++)
06264             if (prequest->valid &&
06265                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06266                /* we found one, so copy it */
06267 
06268                if (pbuf->read_cache_size > 0 &&
06269                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06270                   if (pbuf->read_cache_size - pbuf->read_cache_wp < total_size)
06271                      goto CACHE_FULL;
06272 
06273                   if (pc->read_pointer + total_size <= pheader->size) {
06274                      /* copy event to cache */
06275                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06276                   } else {
06277                      /* event is splitted */
06278                      size = pheader->size - pc->read_pointer;
06279                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06280                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
06281                             pdata, total_size - size);
06282                   }
06283                } else {
06284                   if (pc->read_pointer + total_size <= pheader->size) {
06285                      /* event is not splitted */
06286                      if (total_size > max_size)
06287                         memcpy(destination, pevent, max_size);
06288                      else
06289                         memcpy(destination, pevent, total_size);
06290                   } else {
06291                      /* event is splitted */
06292                      size = pheader->size - pc->read_pointer;
06293 
06294                      if (size > max_size)
06295                         memcpy(destination, pevent, max_size);
06296                      else
06297                         memcpy(destination, pevent, size);
06298 
06299                      if (total_size > max_size) {
06300                         if (size <= max_size)
06301                            memcpy((char *) destination + size, pdata, max_size - size);
06302                      } else
06303                         memcpy((char *) destination + size, pdata, total_size - size);
06304                   }
06305 
06306                   if (total_size < max_size)
06307                      *buf_size = total_size;
06308                   else
06309                      *buf_size = max_size;
06310 
06311                   /* now convert event header */
06312                   if (convert_flags) {
06313                      pevent = (EVENT_HEADER *) destination;
06314                      rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING,
06315                                         convert_flags);
06316                      rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06317                                         convert_flags);
06318                      rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06319                                         convert_flags);
06320                      rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06321                                         convert_flags);
06322                      rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06323                                         convert_flags);
06324                   }
06325                }
06326 
06327                if (pbuf->read_cache_size > 0 &&
06328                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06329                   pbuf->read_cache_wp += total_size;
06330                } else {
06331                   if (total_size > max_size) {
06332                      cm_msg(MERROR, "bm_receive_event",
06333                             "event size larger than buffer size");
06334                      status = BM_TRUNCATED;
06335                   } else
06336                      status = BM_SUCCESS;
06337                }
06338 
06339                /* update statistics */
06340                found = TRUE;
06341                pheader->num_out_events++;
06342                break;
06343             }
06344 
06345          old_read_pointer = pc->read_pointer;
06346 
06347          /* shift read pointer */
06348          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
06349 
06350          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06351             new_read_pointer = 0;
06352 
06353 #ifdef DEBUG_MSG
06354          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06355                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
06356                 total_size);
06357 #endif
06358 
06359          pc->read_pointer = new_read_pointer;
06360 
06361          /*
06362             Repeat until a requested event is found or no more events
06363             are available.
06364           */
06365 
06366          if (pbuf->read_cache_size == 0 && found)
06367             break;
06368 
06369          /* break if event has bypassed read cache */
06370          if (pbuf->read_cache_size > 0 &&
06371              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
06372             break;
06373 
06374       } while (pheader->write_pointer != pc->read_pointer);
06375 
06376     CACHE_FULL:
06377 
06378       /* calculate global read pointer as "minimum" of client read pointers */
06379       min_wp = pheader->write_pointer;
06380 
06381       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06382          if (pctmp->pid) {
06383             if (pctmp->read_pointer < min_wp)
06384                min_wp = pctmp->read_pointer;
06385 
06386             if (pctmp->read_pointer > pheader->write_pointer &&
06387                 pctmp->read_pointer - pheader->size < min_wp)
06388                min_wp = pctmp->read_pointer - pheader->size;
06389          }
06390 
06391       if (min_wp < 0)
06392          min_wp += pheader->size;
06393 
06394       pheader->read_pointer = min_wp;
06395 
06396       /*
06397          If read pointer has been changed, it may have freed up some space
06398          for waiting producers. So check if free space is now more than 50%
06399          of the buffer size and wake waiting producers.
06400        */
06401       size = pc->read_pointer - pheader->write_pointer;
06402       if (size <= 0)
06403          size += pheader->size;
06404 
06405       if (size >= pheader->size * 0.5)
06406          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06407             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
06408                                                              (pctmp->pid == ss_getpid()
06409                                                               && pctmp->tid !=
06410                                                               ss_gettid()))) {
06411 #ifdef DEBUG_MSG
06412                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
06413                       pheader->read_pointer,
06414                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06415 #endif
06416                ss_resume(pctmp->port, "B  ");
06417             }
06418 
06419       /* if no matching event found, start again */
06420       if (!found)
06421          goto LOOP;
06422 
06423       bm_unlock_buffer(buffer_handle);
06424 
06425       if (pbuf->read_cache_size > 0 &&
06426           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
06427          goto CACHE_READ;
06428 
06429       return status;
06430    }
06431 #else                           /* LOCAL_ROUTINES */
06432 
06433    return SS_SUCCESS;
06434 #endif
06435 }
06436 
06437 /********************************************************************/
06438 /**
06439 Skip all events in current buffer.
06440 
06441 Useful for single event displays to see the newest events
06442 @param buffer_handle      Handle of the buffer. Must be obtained
06443                           via bm_open_buffer.
06444 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR 
06445 */
06446 INT bm_skip_event(INT buffer_handle)
06447 {
06448    if (rpc_is_remote())
06449       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
06450 
06451 #ifdef LOCAL_ROUTINES
06452    {
06453       BUFFER *pbuf;
06454       BUFFER_HEADER *pheader;
06455       BUFFER_CLIENT *pclient;
06456 
06457       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06458          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06459          return BM_INVALID_HANDLE;
06460       }
06461 
06462       pbuf = &_buffer[buffer_handle - 1];
06463       pheader = pbuf->buffer_header;
06464 
06465       if (!pbuf->attached) {
06466          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06467          return BM_INVALID_HANDLE;
06468       }
06469 
06470       /* clear cache */
06471       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
06472          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06473 
06474       bm_lock_buffer(buffer_handle);
06475 
06476       /* forward read pointer to global write pointer */
06477       pclient = pheader->client + pbuf->client_index;
06478       pclient->read_pointer = pheader->write_pointer;
06479 
06480       bm_unlock_buffer(buffer_handle);
06481    }
06482 #endif
06483 
06484    return BM_SUCCESS;
06485 }
06486 
06487 /********************************************************************/
06488 /**
06489 Check a buffer if an event is available and call the dispatch function if found.
06490 @param buffer_name       Name of buffer
06491 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, 
06492                     RPC_NET_ERROR
06493 */
06494 INT bm_push_event(char *buffer_name)
06495 {
06496 #ifdef LOCAL_ROUTINES
06497    {
06498       BUFFER *pbuf;
06499       BUFFER_HEADER *pheader;
06500       BUFFER_CLIENT *pclient, *pc, *pctmp;
06501       EVENT_REQUEST *prequest;
06502       EVENT_HEADER *pevent;
06503       char *pdata;
06504       INT i, min_wp, size, total_size, buffer_handle;
06505       INT my_client_index;
06506       BOOL found;
06507       INT old_read_pointer, new_read_pointer;
06508 
06509       for (i = 0; i < _buffer_entries; i++)
06510          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
06511             break;
06512       if (i == _buffer_entries)
06513          return BM_INVALID_HANDLE;
06514 
06515       buffer_handle = i + 1;
06516       pbuf = &_buffer[buffer_handle - 1];
06517 
06518       if (!pbuf->attached)
06519          return BM_INVALID_HANDLE;
06520 
06521       /* return immediately if no callback routine is defined */
06522       if (!pbuf->callback)
06523          return BM_SUCCESS;
06524 
06525       if (_event_buffer_size == 0) {
06526          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
06527          if (_event_buffer == NULL) {
06528             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
06529             return BM_NO_MEMORY;
06530          }
06531          _event_buffer_size = 1000;
06532       }
06533 
06534     CACHE_READ:
06535 
06536       /* look if there is anything in the cache */
06537       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06538          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06539          size = pevent->data_size + sizeof(EVENT_HEADER);
06540 
06541          /* correct size for DWORD boundary */
06542          size = ALIGN8(size);
06543 
06544          /* increment read pointer */
06545          pbuf->read_cache_rp += size;
06546          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06547             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06548 
06549          /* call dispatcher */
06550          for (i = 0; i < _request_list_entries; i++)
06551             if (_request_list[i].buffer_handle == buffer_handle &&
06552                 bm_match_event(_request_list[i].event_id,
06553                                _request_list[i].trigger_mask, pevent)) {
06554                /* if event is fragmented, call defragmenter */
06555                if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
06556                    (pevent->event_id & 0xF000) == EVENTID_FRAG)
06557                   bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1),
06558                                       _request_list[i].dispatcher);
06559                else
06560                   _request_list[i].dispatcher(buffer_handle, i, pevent,
06561                                               (void *) (pevent + 1));
06562             }
06563 
06564          return BM_MORE_EVENTS;
06565       }
06566 
06567       /* calculate some shorthands */
06568       pheader = pbuf->buffer_header;
06569       pdata = (char *) (pheader + 1);
06570       my_client_index = pbuf->client_index;
06571       pclient = pheader->client;
06572       pc = pheader->client + my_client_index;
06573 
06574       /* first do a quick check without locking the buffer */
06575       if (pheader->write_pointer == pc->read_pointer)
06576          return BM_SUCCESS;
06577 
06578       /* lock the buffer */
06579       bm_lock_buffer(buffer_handle);
06580 
06581     LOOP:
06582 
06583       if (pheader->write_pointer == pc->read_pointer) {
06584          bm_unlock_buffer(buffer_handle);
06585 
06586          /* return if no event available */
06587          return BM_SUCCESS;
06588       }
06589 
06590       /* check if event at current read pointer matches a request */
06591       found = FALSE;
06592 
06593       do {
06594          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06595 
06596          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06597          total_size = ALIGN8(total_size);
06598 
06599          prequest = pc->event_request;
06600 
06601          for (i = 0; i < pc->max_request_index; i++, prequest++)
06602             if (prequest->valid &&
06603                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06604                /* we found one, so copy it */
06605 
06606                if (pbuf->read_cache_size > 0 &&
06607                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06608                   /* copy dispatch function and event to cache */
06609 
06610                   if (pbuf->read_cache_size - pbuf->read_cache_wp <
06611                       total_size + (INT) sizeof(void *) + (INT) sizeof(INT))
06612                      goto CACHE_FULL;
06613 
06614                   if (pc->read_pointer + total_size <= pheader->size) {
06615                      /* copy event to cache */
06616                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06617                   } else {
06618                      /* event is splitted */
06619 
06620                      size = pheader->size - pc->read_pointer;
06621                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06622                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
06623                             pdata, total_size - size);
06624                   }
06625 
06626                   pbuf->read_cache_wp += total_size;
06627                } else {
06628                   /* copy event to copy buffer, save dispatcher */
06629 
06630                   if (total_size > _event_buffer_size) {
06631                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
06632                      _event_buffer_size = total_size;
06633                   }
06634 
06635                   if (pc->read_pointer + total_size <= pheader->size) {
06636                      memcpy(_event_buffer, pevent, total_size);
06637                   } else {
06638                      /* event is splitted */
06639                      size = pheader->size - pc->read_pointer;
06640 
06641                      memcpy(_event_buffer, pevent, size);
06642                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
06643                   }
06644                }
06645 
06646                /* update statistics */
06647                found = TRUE;
06648                pheader->num_out_events++;
06649                break;
06650             }
06651 
06652          old_read_pointer = pc->read_pointer;
06653 
06654          /* shift read pointer */
06655          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
06656 
06657          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06658             new_read_pointer = 0;
06659 
06660 #ifdef DEBUG_MSG
06661          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06662                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
06663                 total_size);
06664 #endif
06665 
06666          pc->read_pointer = new_read_pointer;
06667 
06668          /*
06669             Repeat until a requested event is found or no more events
06670             are available or large event received.
06671           */
06672 
06673          if (pbuf->read_cache_size == 0 && found)
06674             break;
06675 
06676          /* break if event has bypassed read cache */
06677          if (pbuf->read_cache_size > 0 &&
06678              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
06679             break;
06680 
06681       } while (pheader->write_pointer != pc->read_pointer);
06682 
06683     CACHE_FULL:
06684 
06685       /* calculate global read pointer as "minimum" of client read pointers */
06686       min_wp = pheader->write_pointer;
06687 
06688       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06689          if (pctmp->pid) {
06690             if (pctmp->read_pointer < min_wp)
06691                min_wp = pctmp->read_pointer;
06692 
06693             if (pctmp->read_pointer > pheader->write_pointer &&
06694                 pctmp->read_pointer - pheader->size < min_wp)
06695                min_wp = pctmp->read_pointer - pheader->size;
06696          }
06697 
06698       if (min_wp < 0)
06699          min_wp += pheader->size;
06700 
06701       pheader->read_pointer = min_wp;
06702 
06703       /*
06704          If read pointer has been changed, it may have freed up some space
06705          for waiting producers. So check if free space is now more than 50%
06706          of the buffer size and wake waiting producers.
06707        */
06708       size = pc->read_pointer - pheader->write_pointer;
06709       if (size <= 0)
06710          size += pheader->size;
06711 
06712       if (size >= pheader->size * 0.5)
06713          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06714             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
06715                                                              (pctmp->pid == ss_getpid()
06716                                                               && pctmp->tid !=
06717                                                               ss_gettid()))) {
06718 #ifdef DEBUG_MSG
06719                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
06720                       pheader->read_pointer,
06721                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06722 #endif
06723                ss_resume(pctmp->port, "B  ");
06724             }
06725 
06726       /* if no matching event found, start again */
06727       if (!found)
06728          goto LOOP;
06729 
06730       bm_unlock_buffer(buffer_handle);
06731 
06732       if (pbuf->read_cache_size > 0 &&
06733           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
06734          goto CACHE_READ;
06735 
06736       /* call dispatcher */
06737       for (i = 0; i < _request_list_entries; i++)
06738          if (_request_list[i].buffer_handle == buffer_handle &&
06739              bm_match_event(_request_list[i].event_id,
06740                             _request_list[i].trigger_mask, _event_buffer)) {
06741             if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
06742                 (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
06743                bm_defragment_event(buffer_handle, i, _event_buffer,
06744                                    (void *) (((EVENT_HEADER *) _event_buffer) + 1),
06745                                    _request_list[i].dispatcher);
06746             else
06747                _request_list[i].dispatcher(buffer_handle, i, _event_buffer,
06748                                            (void *) (((EVENT_HEADER *) _event_buffer) +
06749                                                      1));
06750          }
06751 
06752       return BM_MORE_EVENTS;
06753    }
06754 #else                           /* LOCAL_ROUTINES */
06755 
06756    return BM_SUCCESS;
06757 #endif
06758 }
06759 
06760 /********************************************************************/
06761 /**
06762 Check if any requested event is waiting in a buffer
06763 @return TRUE             More events are waiting<br>
06764         FALSE            No more events are waiting
06765 */
06766 INT bm_check_buffers()
06767 {
06768 #ifdef LOCAL_ROUTINES
06769    {
06770       INT index, status = 0;
06771       INT server_type, server_conn, tid;
06772       BOOL bMore;
06773       DWORD start_time;
06774 
06775       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
06776       server_conn = rpc_get_server_acception();
06777       tid = ss_gettid();
06778 
06779       /* if running as a server, buffer checking is done by client
06780          via ASYNC bm_receive_event */
06781       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
06782          return FALSE;
06783 
06784       bMore = FALSE;
06785       start_time = ss_millitime();
06786 
06787       /* go through all buffers */
06788       for (index = 0; index < _buffer_entries; index++) {
06789          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
06790             continue;
06791 
06792          if (server_type != ST_SINGLE && _buffer[index].index != tid)
06793             continue;
06794 
06795          if (!_buffer[index].attached)
06796             continue;
06797 
06798          do {
06799 
06800             /* one bm_push_event could cause a run stop and a buffer close, which
06801                would crash the next call to bm_push_event(). So check for valid
06802                buffer on each call */
06803             if (index < _buffer_entries && _buffer[index].buffer_header->name != NULL)
06804                status = bm_push_event(_buffer[index].buffer_header->name);
06805 
06806             if (status != BM_MORE_EVENTS)
06807                break;
06808 
06809             /* stop after one second */
06810             if (ss_millitime() - start_time > 1000) {
06811                bMore = TRUE;
06812                break;
06813             }
06814 
06815          } while (TRUE);
06816       }
06817 
06818       return bMore;
06819 
06820    }
06821 #else                           /* LOCAL_ROUTINES */
06822 
06823    return FALSE;
06824 
06825 #endif
06826 }
06827 
06828 /**dox***************************************************************/
06829 #ifndef DOXYGEN_SHOULD_SKIP_THIS
06830 
06831 /********************************************************************/
06832 INT bm_mark_read_waiting(BOOL flag)
06833 /********************************************************************\
06834 
06835   Routine: bm_mark_read_waiting
06836 
06837   Purpose: Mark all open buffers ready for receiving events.
06838            Called internally by ss_suspend
06839 
06840 
06841   Input:
06842     BOOL flag               TRUE for waiting, FALSE for not waiting
06843 
06844   Output:
06845     none
06846 
06847   Function value:
06848     BM_SUCCESS              Successful completion
06849 
06850 \********************************************************************/
06851 {
06852    if (rpc_is_remote())
06853       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
06854 
06855 #ifdef LOCAL_ROUTINES
06856    {
06857       INT i;
06858       BUFFER_HEADER *pheader;
06859       BUFFER_CLIENT *pclient;
06860 
06861       /* Mark all buffer for read waiting */
06862       for (i = 0; i < _buffer_entries; i++) {
06863          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
06864              _buffer[i].index != rpc_get_server_acception())
06865             continue;
06866 
06867          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
06868              _buffer[i].index != ss_gettid())
06869             continue;
06870 
06871          if (!_buffer[i].attached)
06872             continue;
06873 
06874          pheader = _buffer[i].buffer_header;
06875          pclient = pheader->client + _buffer[i].client_index;
06876          pclient->read_wait = flag;
06877       }
06878    }
06879 #endif                          /* LOCAL_ROUTINES */
06880 
06881    return BM_SUCCESS;
06882 }
06883 
06884 /********************************************************************/
06885 INT bm_notify_client(char *buffer_name, int socket)
06886 /********************************************************************\
06887 
06888   Routine: bm_notify_client
06889 
06890   Purpose: Called by cm_dispatch_ipc. Send an event notification to
06891            the connected client
06892 
06893   Input:
06894     char  *buffer_name      Name of buffer
06895     int   socket            Network socket to client
06896 
06897   Output:
06898     none
06899 
06900   Function value:
06901     BM_SUCCESS              Successful completion
06902 
06903 \********************************************************************/
06904 {
06905    char buffer[32];
06906    NET_COMMAND *nc;
06907    INT i, convert_flags;
06908    static DWORD last_time = 0;
06909 
06910    for (i = 0; i < _buffer_entries; i++)
06911       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
06912          break;
06913    if (i == _buffer_entries)
06914       return BM_INVALID_HANDLE;
06915 
06916    /* don't send notification if client has no callback defined
06917       to receive events -> client calls bm_receive_event manually */
06918    if (!_buffer[i].callback)
06919       return DB_SUCCESS;
06920 
06921    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06922 
06923    /* only send notification once each 500ms */
06924    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
06925       return DB_SUCCESS;
06926 
06927    last_time = ss_millitime();
06928 
06929    if (convert_flags & CF_ASCII) {
06930       sprintf(buffer, "MSG_BM&%s", buffer_name);
06931       send_tcp(socket, buffer, strlen(buffer) + 1, 0);
06932    } else {
06933       nc = (NET_COMMAND *) buffer;
06934 
06935       nc->header.routine_id = MSG_BM;
06936       nc->header.param_size = 0;
06937 
06938       if (convert_flags) {
06939          rpc_convert_single(&nc->header.routine_id, TID_DWORD,
06940                             RPC_OUTGOING, convert_flags);
06941          rpc_convert_single(&nc->header.param_size, TID_DWORD,
06942                             RPC_OUTGOING, convert_flags);
06943       }
06944 
06945       /* send the update notification to the client */
06946       send_tcp(socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
06947    }
06948 
06949    return BM_SUCCESS;
06950 }
06951 
06952 /********************************************************************/
06953 INT bm_poll_event(INT flag)
06954 /********************************************************************\
06955 
06956   Routine: bm_poll_event
06957 
06958   Purpose: Poll an event from a remote server. Gets called by
06959            rpc_client_dispatch
06960 
06961   Input:
06962     INT flag         TRUE if called from cm_yield
06963 
06964   Output:
06965     none
06966 
06967   Function value:
06968     TRUE             More events are waiting
06969     FALSE            No more events are waiting
06970     SS_ABORT         Network connection broken
06971 
06972 \********************************************************************/
06973 {
06974    INT status, size, i, request_id;
06975    DWORD start_time;
06976    BOOL bMore;
06977    static BOOL bMoreLast = FALSE;
06978 
06979    if (_event_buffer_size == 0) {
06980       _event_buffer = (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
06981       if (!_event_buffer) {
06982          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer");
06983          return SS_ABORT;
06984       }
06985       _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
06986    }
06987 
06988    start_time = ss_millitime();
06989 
06990    /* if we got event notification, turn off read_wait */
06991    if (!flag)
06992       bm_mark_read_waiting(FALSE);
06993 
06994    /* if called from yield, return if no more events */
06995    if (flag) {
06996       if (!bMoreLast)
06997          return FALSE;
06998    }
06999 
07000    bMore = FALSE;
07001 
07002    /* loop over all requests */
07003    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07004       /* continue if no dispatcher set (manual bm_receive_event) */
07005       if (_request_list[request_id].dispatcher == NULL)
07006          continue;
07007 
07008       do {
07009          /* receive event */
07010          size = _event_buffer_size;
07011          status = bm_receive_event(_request_list[request_id].buffer_handle,
07012                                    _event_buffer, &size, ASYNC);
07013 
07014          /* call user function if successful */
07015          if (status == BM_SUCCESS)
07016             /* search proper request for this event */
07017             for (i = 0; i < _request_list_entries; i++)
07018                if ((_request_list[i].buffer_handle ==
07019                     _request_list[request_id].buffer_handle) &&
07020                    bm_match_event(_request_list[i].event_id,
07021                                   _request_list[i].trigger_mask, _event_buffer)) {
07022                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07023                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07024                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07025                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07026                                          _request_list[i].dispatcher);
07027                   else
07028                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07029                                                  _event_buffer,
07030                                                  (void
07031                                                   *) (((EVENT_HEADER *) _event_buffer) +
07032                                                       1));
07033                }
07034 
07035          /* break if no more events */
07036          if (status == BM_ASYNC_RETURN)
07037             break;
07038 
07039          /* break if server died */
07040          if (status == RPC_NET_ERROR)
07041             return SS_ABORT;
07042 
07043          /* stop after one second */
07044          if (ss_millitime() - start_time > 1000) {
07045             bMore = TRUE;
07046             break;
07047          }
07048 
07049       } while (TRUE);
07050    }
07051 
07052    if (!bMore)
07053       bm_mark_read_waiting(TRUE);
07054 
07055    bMoreLast = bMore;
07056 
07057    return bMore;
07058 }
07059 
07060 /**dox***************************************************************/
07061 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07062 
07063 /********************************************************************/
07064 /** 
07065 Clears event buffer and cache.
07066 If an event buffer is large and a consumer is slow in analyzing
07067 events, events are usually received some time after they are produced.
07068 This effect is even more experienced if a read cache is used
07069 (via bm_set_cache_size()).
07070 When changes to the hardware are made in the experience, the consumer will then
07071 still analyze old events before any new event which reflects the hardware change.
07072 Users can be fooled by looking at histograms which reflect the hardware change
07073 many seconds after they have been made.
07074 
07075 To overcome this potential problem, the analyzer can call
07076 bm_empty_buffers() just after the hardware change has been made which
07077 skips all old events contained in event buffers and read caches.
07078 Technically this is done by forwarding the read pointer of the client.
07079 No events are really deleted, they are still visible to other clients like
07080 the logger.
07081 
07082 Note that the front-end also contains write buffers which can delay the
07083 delivery of events.
07084 The standard front-end framework mfe.c reduces this effect by flushing
07085 all buffers once every second.
07086 @return BM_SUCCESS
07087 */
07088 INT bm_empty_buffers()
07089 {
07090    if (rpc_is_remote())
07091       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07092 
07093 #ifdef LOCAL_ROUTINES
07094    {
07095       INT index, server_type, server_conn, tid;
07096       BUFFER *pbuf;
07097       BUFFER_CLIENT *pclient;
07098 
07099       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07100       server_conn = rpc_get_server_acception();
07101       tid = ss_gettid();
07102 
07103       /* go through all buffers */
07104       for (index = 0; index < _buffer_entries; index++) {
07105          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07106             continue;
07107 
07108          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07109             continue;
07110 
07111          if (!_buffer[index].attached)
07112             continue;
07113 
07114          pbuf = &_buffer[index];
07115 
07116          /* empty cache */
07117          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07118 
07119          /* set read pointer to write pointer */
07120          pclient = (pbuf->buffer_header)->client + pbuf->client_index;
07121          bm_lock_buffer(index + 1);
07122          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07123          bm_unlock_buffer(index + 1);
07124       }
07125 
07126    }
07127 #endif                          /* LOCAL_ROUTINES */
07128 
07129    return BM_SUCCESS;
07130 }
07131 
07132 /**dox***************************************************************/
07133 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07134 
07135 #define MAX_DEFRAG_EVENTS 10
07136 
07137 typedef struct {
07138    WORD event_id;
07139    DWORD data_size;
07140    DWORD received;
07141    EVENT_HEADER *pevent;
07142 } EVENT_DEFRAG_BUFFER;
07143 
07144 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07145 
07146 /********************************************************************/
07147 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07148                          EVENT_HEADER * pevent, void *pdata,
07149                          void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *, void *))
07150 /********************************************************************\
07151 
07152   Routine: bm_defragment_event
07153 
07154   Purpose: Called internally from the event receiving routines
07155            bm_push_event and bm_poll_event to recombine event
07156            fragments and call the user callback routine upon
07157            completion.
07158 
07159   Input:
07160     HNDLE buffer_handle  Handle for the buffer containing event
07161     HNDLE request_id     Handle for event request
07162     EVENT_HEADER *pevent Pointer to event header
07163     void *pata           Pointer to event data
07164     dispatcher()         User callback routine
07165 
07166   Output:
07167     <calls dispatcher() after successfull recombination of event>
07168 
07169   Function value:
07170     void
07171 
07172 \********************************************************************/
07173 {
07174    INT i;
07175    static int j = -1;
07176 
07177    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07178     /*---- start new event ----*/
07179 
07180       printf("First Frag detected : Ser#:%ld ID=0x%x \n", pevent->serial_number,
07181              pevent->event_id);
07182       /* check if fragments already stored */
07183       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07184          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07185             break;
07186 
07187       if (i < MAX_DEFRAG_EVENTS) {
07188          free(defrag_buffer[i].pevent);
07189          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07190          cm_msg(MERROR, "bm_defragement_event",
07191                 "Received new event with ID %d while old fragments were not completed",
07192                 (pevent->event_id & 0x0FFF));
07193       }
07194 
07195       /* search new slot */
07196       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07197          if (defrag_buffer[i].event_id == 0)
07198             break;
07199 
07200       if (i == MAX_DEFRAG_EVENTS) {
07201          cm_msg(MERROR, "bm_defragment_event",
07202                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07203          return;
07204       }
07205 
07206       /* check event size */
07207       if (pevent->data_size != sizeof(DWORD)) {
07208          cm_msg(MERROR, "bm_defragment_event",
07209                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07210                 pevent->data_size, sizeof(DWORD));
07211          return;
07212       }
07213 
07214       /* setup defragment buffer */
07215       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07216       defrag_buffer[i].data_size = *(DWORD *) pdata;
07217       defrag_buffer[i].received = 0;
07218       defrag_buffer[i].pevent =
07219           (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07220 
07221       if (defrag_buffer[i].pevent == NULL) {
07222          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07223          cm_msg(MERROR, "bm_defragement_event",
07224                 "Not enough memory to allocate event defragment buffer");
07225          return;
07226       }
07227 
07228       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07229       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07230       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07231 
07232       printf("First frag[%d] (ID %d) Ser#:%ld sz:%ld\n", i, defrag_buffer[i].event_id,
07233              pevent->serial_number, defrag_buffer[i].data_size);
07234       j = 0;
07235 
07236       return;
07237    }
07238 
07239    /* search buffer for that event */
07240    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07241       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07242          break;
07243 
07244    if (i == MAX_DEFRAG_EVENTS) {
07245       /* no buffer available -> no first fragment received */
07246       free(defrag_buffer[i].pevent);
07247       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07248       cm_msg(MERROR, "bm_defragement_event",
07249              "Received fragment without first fragment (ID %d) Ser#:%d",
07250              pevent->event_id & 0x0FFF, pevent->serial_number);
07251       printf("Received fragment without first fragment (ID 0x%x) Ser#:%ld Sz:%ld\n",
07252              pevent->event_id, pevent->serial_number, pevent->data_size);
07253       return;
07254    }
07255 
07256    /* add fragment to buffer */
07257    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
07258       free(defrag_buffer[i].pevent);
07259       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07260       cm_msg(MERROR, "bm_defragement_event",
07261              "Received fragments with more data (%d) than event size (%d)",
07262              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
07263       return;
07264    }
07265 
07266    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07267           defrag_buffer[i].received, pdata, pevent->data_size);
07268 
07269    defrag_buffer[i].received += pevent->data_size;
07270 
07271    printf("Other frag[%d][%d] (ID %d) Ser#:%ld sz:%ld\n", i, j++,
07272           defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
07273 
07274 
07275    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
07276       /* event complete */
07277       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent,
07278                  defrag_buffer[i].pevent + 1);
07279       free(defrag_buffer[i].pevent);
07280       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07281    }
07282 }
07283 
07284 /**dox***************************************************************/
07285 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07286 
07287 /**dox***************************************************************/
07288                    /** @} *//* end of bmfunctionc */
07289 
07290 /**dox***************************************************************/
07291 /** @addtogroup rpcfunctionc
07292  *  
07293  *  @{  */
07294 
07295 /**dox***************************************************************/
07296 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07297 
07298 /********************************************************************\
07299 *                                                                    *
07300 *                         RPC functions                              *
07301 *                                                                    *
07302 \********************************************************************/
07303 
07304 /* globals */
07305 
07306 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
07307 RPC_SERVER_CONNECTION _server_connection;
07308 
07309 static int _lsock;
07310 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
07311 static INT _server_acception_index = 0;
07312 static INT _server_type;
07313 static char _server_name[256];
07314 
07315 static RPC_LIST *rpc_list = NULL;
07316 
07317 int _opt_tcp_size = OPT_TCP_SIZE;
07318 
07319 
07320 /********************************************************************\
07321 *                       conversion functions                         *
07322 \********************************************************************/
07323 
07324 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
07325 {
07326    *convert_flags = 0;
07327 
07328    /* big/little endian conversion */
07329    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
07330         (hw_type & DRI_LITTLE_ENDIAN)) ||
07331        ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
07332       *convert_flags |= CF_ENDIAN;
07333 
07334    /* float conversion between IEEE and VAX G */
07335    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
07336       *convert_flags |= CF_VAX2IEEE;
07337 
07338    /* float conversion between VAX G and IEEE */
07339    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
07340       *convert_flags |= CF_IEEE2VAX;
07341 
07342    /* ASCII format */
07343    if (remote_hw_type & DR_ASCII)
07344       *convert_flags |= CF_ASCII;
07345 }
07346 
07347 /********************************************************************/
07348 void rpc_get_convert_flags(INT * convert_flags)
07349 {
07350    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE),
07351                           _server_connection.remote_hw_type, convert_flags);
07352 }
07353 
07354 /********************************************************************/
07355 void rpc_ieee2vax_float(float *var)
07356 {
07357    unsigned short int lo, hi;
07358 
07359    /* swap hi and lo word */
07360    lo = *((short int *) (var) + 1);
07361    hi = *((short int *) (var));
07362 
07363    /* correct exponent */
07364    if (lo != 0)
07365       lo += 0x100;
07366 
07367    *((short int *) (var) + 1) = hi;
07368    *((short int *) (var)) = lo;
07369 }
07370 
07371 void rpc_vax2ieee_float(float *var)
07372 {
07373    unsigned short int lo, hi;
07374 
07375    /* swap hi and lo word */
07376    lo = *((short int *) (var) + 1);
07377    hi = *((short int *) (var));
07378 
07379    /* correct exponent */
07380    if (hi != 0)
07381       hi -= 0x100;
07382 
07383    *((short int *) (var) + 1) = hi;
07384    *((short int *) (var)) = lo;
07385 
07386 }
07387 
07388 void rpc_vax2ieee_double(double *var)
07389 {
07390    unsigned short int i1, i2, i3, i4;
07391 
07392    /* swap words */
07393    i1 = *((short int *) (var) + 3);
07394    i2 = *((short int *) (var) + 2);
07395    i3 = *((short int *) (var) + 1);
07396    i4 = *((short int *) (var));
07397 
07398    /* correct exponent */
07399    if (i4 != 0)
07400       i4 -= 0x20;
07401 
07402    *((short int *) (var) + 3) = i4;
07403    *((short int *) (var) + 2) = i3;
07404    *((short int *) (var) + 1) = i2;
07405    *((short int *) (var)) = i1;
07406 }
07407 
07408 void rpc_ieee2vax_double(double *var)
07409 {
07410    unsigned short int i1, i2, i3, i4;
07411 
07412    /* swap words */
07413    i1 = *((short int *) (var) + 3);
07414    i2 = *((short int *) (var) + 2);
07415    i3 = *((short int *) (var) + 1);
07416    i4 = *((short int *) (var));
07417 
07418    /* correct exponent */
07419    if (i1 != 0)
07420       i1 += 0x20;
07421 
07422    *((short int *) (var) + 3) = i4;
07423    *((short int *) (var) + 2) = i3;
07424    *((short int *) (var) + 1) = i2;
07425    *((short int *) (var)) = i1;
07426 }
07427 
07428 /********************************************************************/
07429 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
07430 {
07431 
07432    if (convert_flags & CF_ENDIAN) {
07433       if (tid == TID_WORD || tid == TID_SHORT)
07434          WORD_SWAP(data);
07435       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
07436          DWORD_SWAP(data);
07437       if (tid == TID_DOUBLE)
07438          QWORD_SWAP(data);
07439    }
07440 
07441    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
07442        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
07443       if (tid == TID_FLOAT)
07444          rpc_ieee2vax_float((float *) data);
07445       if (tid == TID_DOUBLE)
07446          rpc_ieee2vax_double((double *) data);
07447    }
07448 
07449    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
07450        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
07451       if (tid == TID_FLOAT)
07452          rpc_vax2ieee_float((float *) data);
07453       if (tid == TID_DOUBLE)
07454          rpc_vax2ieee_double((double *) data);
07455    }
07456 }
07457 
07458 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
07459 /********************************************************************\
07460 
07461   Routine: rpc_convert_data
07462 
07463   Purpose: Convert data format between differenct computers
07464 
07465   Input:
07466     void   *data            Pointer to data
07467     INT    tid              Type ID of data, one of TID_xxx
07468     INT    flags            Combination of following flags:
07469                               RPC_IN: data is input parameter
07470                               RPC_OUT: data is output variable
07471                               RPC_FIXARRAY, RPC_VARARRAY: data is array
07472                                 of "size" bytes (see next param.)
07473                               RPC_OUTGOING: data is outgoing
07474     INT    total_size       Size of bytes of data. Used for variable
07475                             length arrays.
07476     INT    convert_flags    Flags for data conversion
07477 
07478   Output:
07479     void   *data            Is converted according to _convert_flag
07480                             value
07481 
07482   Function value:
07483     RPC_SUCCESS             Successful completion
07484 
07485 \********************************************************************/
07486 {
07487    INT i, n, single_size;
07488    char *p;
07489 
07490    /* convert array */
07491    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
07492       single_size = tid_size[tid];
07493       /* don't convert TID_ARRAY & TID_STRUCT */
07494       if (single_size == 0)
07495          return;
07496 
07497       n = total_size / single_size;
07498 
07499       for (i = 0; i < n; i++) {
07500          p = (char *) data + (i * single_size);
07501          rpc_convert_single(p, tid, flags, convert_flags);
07502       }
07503    } else {
07504       rpc_convert_single(data, tid, flags, convert_flags);
07505    }
07506 }
07507 
07508 /********************************************************************\
07509 *                       type ID functions                            *
07510 \********************************************************************/
07511 
07512 INT rpc_tid_size(INT id)
07513 {
07514    if (id < TID_LAST)
07515       return tid_size[id];
07516 
07517    return 0;
07518 }
07519 
07520 char *rpc_tid_name(INT id)
07521 {
07522    if (id < TID_LAST)
07523       return tid_name[id];
07524    else
07525       return "<unknown>";
07526 }
07527 
07528 
07529 /**dox***************************************************************/
07530 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07531 
07532 /********************************************************************\
07533 *                        client functions                            *
07534 \********************************************************************/
07535 
07536 /********************************************************************/
07537 /**
07538 Register RPC client for standalone mode (without standard
07539            midas server)
07540 @param list           Array of RPC_LIST structures containing
07541                             function IDs and parameter definitions.
07542                             The end of the list must be indicated by
07543                             a function ID of zero.
07544 @param name          Name of this client
07545 @return RPC_SUCCESS
07546 */
07547 INT rpc_register_client(char *name, RPC_LIST * list)
07548 {
07549    rpc_set_name(name);
07550    rpc_register_functions(rpc_get_internal_list(0), NULL);
07551    rpc_register_functions(list, NULL);
07552 
07553    return RPC_SUCCESS;
07554 }
07555 
07556 /********************************************************************/
07557 /**
07558 Register a set of RPC functions (both as clients or servers)
07559 @param new_list       Array of RPC_LIST structures containing
07560                             function IDs and parameter definitions.
07561                             The end of the list must be indicated by
07562                             a function ID of zero.
07563 @param func          Default dispatch function
07564 
07565 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
07566 */
07567 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
07568 {
07569    INT i, j, iold, inew;
07570 
07571    /* count number of new functions */
07572    for (i = 0; new_list[i].id != 0; i++) {
07573       /* check double defined functions */
07574       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
07575          if (rpc_list[j].id == new_list[i].id)
07576             return RPC_DOUBLE_DEFINED;
07577    }
07578    inew = i;
07579 
07580    /* count number of existing functions */
07581    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
07582    iold = i;
07583 
07584    /* allocate new memory for rpc_list */
07585    if (rpc_list == NULL)
07586       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
07587    else
07588       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
07589 
07590    if (rpc_list == NULL) {
07591       cm_msg(MERROR, "rpc_register_functions", "out of memory");
07592       return RPC_NO_MEMORY;
07593    }
07594 
07595    /* append new functions */
07596    for (i = iold; i < iold + inew; i++) {
07597       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
07598 
07599       /* set default dispatcher */
07600       if (rpc_list[i].dispatch == NULL)
07601          rpc_list[i].dispatch = func;
07602 
07603       /* check valid ID for user functions */
07604       if (new_list != rpc_get_internal_list(0) &&
07605           new_list != rpc_get_internal_list(1) &&
07606           (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
07607          cm_msg(MERROR, "rpc_register_functions",
07608                 "registered RPC function with invalid ID");
07609    }
07610 
07611    /* mark end of list */
07612    rpc_list[i].id = 0;
07613 
07614    return RPC_SUCCESS;
07615 }
07616 
07617 
07618 
07619 /**dox***************************************************************/
07620 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07621 
07622 /********************************************************************/
07623 INT rpc_deregister_functions()
07624 /********************************************************************\
07625 
07626   Routine: rpc_deregister_functions
07627 
07628   Purpose: Free memory of previously registered functions
07629 
07630   Input:
07631     none
07632 
07633   Output:
07634     none
07635 
07636   Function value:
07637     RPC_SUCCESS              Successful completion
07638 
07639 \********************************************************************/
07640 {
07641    if (rpc_list)
07642       M_FREE(rpc_list);
07643    rpc_list = NULL;
07644 
07645    return RPC_SUCCESS;
07646 }
07647 
07648 
07649 /********************************************************************/
07650 INT rpc_register_function(INT id, INT(*func) (INT, void **))
07651 /********************************************************************\
07652 
07653   Routine: rpc_register_function
07654 
07655   Purpose: Replace a dispatch function for a specific rpc routine
07656 
07657   Input:
07658     INT      id             RPC ID
07659     INT      *func          New dispatch function
07660 
07661   Output:
07662    <implicit: func gets copied to rpc_list>
07663 
07664   Function value:
07665    RPC_SUCCESS              Successful completion
07666    RPC_INVALID_ID           RPC ID not found
07667 
07668 \********************************************************************/
07669 {
07670    INT i;
07671 
07672    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
07673       if (rpc_list[i].id == id)
07674          break;
07675 
07676    if (rpc_list[i].id == id)
07677       rpc_list[i].dispatch = func;
07678    else
07679       return RPC_INVALID_ID;
07680 
07681    return RPC_SUCCESS;
07682 }
07683 
07684 
07685 /********************************************************************/
07686 INT rpc_client_dispatch(int sock)
07687 /********************************************************************\
07688 
07689   Routine: rpc_client_dispatch
07690 
07691   Purpose: Gets called whenever a client receives data from the
07692            server. Get set via rpc_connect. Internal use only.
07693 
07694 \********************************************************************/
07695 {
07696    INT hDB, hKey, n;
07697    NET_COMMAND *nc;
07698    INT status = 0;
07699    char net_buffer[256];
07700 
07701    nc = (NET_COMMAND *) net_buffer;
07702 
07703    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07704    if (n <= 0)
07705       return SS_ABORT;
07706 
07707    if (nc->header.routine_id == MSG_ODB) {
07708       /* update a changed record */
07709       hDB = *((INT *) nc->param);
07710       hKey = *((INT *) nc->param + 1);
07711       status = db_update_record(hDB, hKey, 0);
07712    }
07713 
07714    else if (nc->header.routine_id == MSG_WATCHDOG) {
07715       nc->header.routine_id = 1;
07716       nc->header.param_size = 0;
07717       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07718       status = RPC_SUCCESS;
07719    }
07720 
07721    else if (nc->header.routine_id == MSG_BM) {
07722       fd_set readfds;
07723       struct timeval timeout;
07724 
07725       /* receive further messages to empty TCP queue */
07726       do {
07727          FD_ZERO(&readfds);
07728          FD_SET(sock, &readfds);
07729 
07730          timeout.tv_sec = 0;
07731          timeout.tv_usec = 0;
07732 
07733          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
07734 
07735          if (FD_ISSET(sock, &readfds)) {
07736             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07737             if (n <= 0)
07738                return SS_ABORT;
07739 
07740             if (nc->header.routine_id == MSG_ODB) {
07741                /* update a changed record */
07742                hDB = *((INT *) nc->param);
07743                hKey = *((INT *) nc->param + 1);
07744                status = db_update_record(hDB, hKey, 0);
07745             }
07746 
07747             else if (nc->header.routine_id == MSG_WATCHDOG) {
07748                nc->header.routine_id = 1;
07749                nc->header.param_size = 0;
07750                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07751                status = RPC_SUCCESS;
07752             }
07753          }
07754 
07755       } while (FD_ISSET(sock, &readfds));
07756 
07757       /* poll event from server */
07758       status = bm_poll_event(FALSE);
07759    }
07760 
07761    return status;
07762 }
07763 
07764 
07765 /********************************************************************/
07766 INT rpc_client_connect(char *host_name, INT port, char *client_name, HNDLE * hConnection)
07767 /********************************************************************\
07768 
07769   Routine: rpc_client_connect
07770 
07771   Purpose: Establish a network connection to a remote client
07772 
07773   Input:
07774     char *host_name          IP address of host to connect to.
07775     INT  port                TPC port to connect to.
07776     char *clinet_name        Client program name
07777 
07778   Output:
07779     HNDLE *hConnection       Handle for new connection which can be used
07780                              in future rpc_call(hConnection....) calls
07781 
07782   Function value:
07783     RPC_SUCCESS              Successful completion
07784     RPC_NET_ERROR            Error in socket call
07785     RPC_NO_CONNECTION        Maximum number of connections reached
07786     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
07787 
07788 \********************************************************************/
07789 {
07790    INT i, status, index;
07791    struct sockaddr_in bind_addr;
07792    INT sock;
07793    INT remote_hw_type, hw_type;
07794    char str[200];
07795    char version[32], v1[32];
07796    char local_prog_name[NAME_LENGTH];
07797    char local_host_name[HOST_NAME_LENGTH];
07798    struct hostent *phe;
07799 
07800 #ifdef OS_WINNT
07801    {
07802       WSADATA WSAData;
07803 
07804       /* Start windows sockets */
07805       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
07806          return RPC_NET_ERROR;
07807    }
07808 #endif
07809 
07810    /* check if cm_connect_experiment was called */
07811    if (_client_name[0] == 0) {
07812       cm_msg(MERROR, "rpc_client_connect",
07813              "cm_connect_experiment/rpc_set_name not called");
07814       return RPC_NOT_REGISTERED;
07815    }
07816 
07817    /* check for broken connections */
07818    rpc_client_check();
07819 
07820    /* check if connection already exists */
07821    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07822       if (_client_connection[i].send_sock != 0 &&
07823           strcmp(_client_connection[i].host_name, host_name) == 0 &&
07824           _client_connection[i].port == port) {
07825          *hConnection = i + 1;
07826          return RPC_SUCCESS;
07827       }
07828 
07829    /* search for free entry */
07830    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07831       if (_client_connection[i].send_sock == 0)
07832          break;
07833 
07834    /* open new network connection */
07835    if (i == MAX_RPC_CONNECTION) {
07836       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
07837       return RPC_NO_CONNECTION;
07838    }
07839 
07840    /* create a new socket for connecting to remote server */
07841    sock = socket(AF_INET, SOCK_STREAM, 0);
07842    if (sock == -1) {
07843       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
07844       return RPC_NET_ERROR;
07845    }
07846 
07847    index = i;
07848    strcpy(_client_connection[index].host_name, host_name);
07849    strcpy(_client_connection[index].client_name, client_name);
07850    _client_connection[index].port = port;
07851    _client_connection[index].exp_name[0] = 0;
07852    _client_connection[index].transport = RPC_TCP;
07853    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
07854    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
07855 
07856    /* connect to remote node */
07857    memset(&bind_addr, 0, sizeof(bind_addr));
07858    bind_addr.sin_family = AF_INET;
07859    bind_addr.sin_addr.s_addr = 0;
07860    bind_addr.sin_port = htons((short) port);
07861 
07862 #ifdef OS_VXWORKS
07863    {
07864       INT host_addr;
07865 
07866       host_addr = hostGetByName(host_name);
07867       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
07868    }
07869 #else
07870    phe = gethostbyname(host_name);
07871    if (phe == NULL) {
07872       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
07873       return RPC_NET_ERROR;
07874    }
07875    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
07876 #endif
07877 
07878 #ifdef OS_UNIX
07879    do {
07880       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
07881 
07882       /* don't return if an alarm signal was cought */
07883    } while (status == -1 && errno == EINTR);
07884 #else
07885    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
07886 #endif
07887 
07888    if (status != 0) {
07889       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
07890          message should be displayed by application */
07891       return RPC_NET_ERROR;
07892    }
07893 
07894    /* set TCP_NODELAY option for better performance */
07895 #ifdef OS_VXWORKS
07896    i = 1;
07897    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
07898 #endif
07899 
07900    /* send local computer info */
07901    rpc_get_name(local_prog_name);
07902    gethostname(local_host_name, sizeof(local_host_name));
07903 
07904    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
07905    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name,
07906            local_host_name);
07907 
07908    send(sock, str, strlen(str) + 1, 0);
07909 
07910    /* receive remote computer info */
07911    i = recv_string(sock, str, sizeof(str), 10000);
07912    if (i <= 0) {
07913       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s",
07914              str);
07915       return RPC_NET_ERROR;
07916    }
07917 
07918    remote_hw_type = version[0] = 0;
07919    sscanf(str, "%d %s", &remote_hw_type, version);
07920    _client_connection[index].remote_hw_type = remote_hw_type;
07921    _client_connection[index].send_sock = sock;
07922 
07923    /* print warning if version patch level doesn't agree */
07924    strcpy(v1, version);
07925    if (strchr(v1, '.'))
07926       if (strchr(strchr(v1, '.') + 1, '.'))
07927          *strchr(strchr(v1, '.') + 1, '.') = 0;
07928 
07929    strcpy(str, cm_get_version());
07930    if (strchr(str, '.'))
07931       if (strchr(strchr(str, '.') + 1, '.'))
07932          *strchr(strchr(str, '.') + 1, '.') = 0;
07933 
07934    if (strcmp(v1, str) != 0) {
07935       sprintf(str, "remote MIDAS version %s differs from local version %s",
07936               version, cm_get_version());
07937       cm_msg(MERROR, "rpc_client_connect", str);
07938    }
07939 
07940    *hConnection = index + 1;
07941 
07942    return RPC_SUCCESS;
07943 }
07944 
07945 
07946 /********************************************************************/
07947 void rpc_client_check()
07948 /********************************************************************\
07949 
07950   Routine: rpc_client_check
07951 
07952   Purpose: Check all client connections if remote client closed link
07953 
07954   Function value:
07955     RPC_SUCCESS              Successful completion
07956     RPC_NET_ERROR            Error in socket call
07957     RPC_NO_CONNECTION        Maximum number of connections reached
07958     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
07959 
07960 \********************************************************************/
07961 {
07962    INT i, status;
07963 
07964    /* check for broken connections */
07965    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07966       if (_client_connection[i].send_sock != 0) {
07967          int sock;
07968          fd_set readfds;
07969          struct timeval timeout;
07970          char buffer[64];
07971 
07972          sock = _client_connection[i].send_sock;
07973          FD_ZERO(&readfds);
07974          FD_SET(sock, &readfds);
07975 
07976          timeout.tv_sec = 0;
07977          timeout.tv_usec = 0;
07978 
07979          do {
07980             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
07981          } while (status == -1);        /* dont return if an alarm signal was cought */
07982 
07983          if (FD_ISSET(sock, &readfds)) {
07984             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
07985 
07986             if (equal_ustring(buffer, "EXIT")) {
07987                /* normal exit */
07988                closesocket(sock);
07989                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
07990             }
07991 
07992             if (status <= 0) {
07993                cm_msg(MERROR, "rpc_client_check",
07994                       "Connection broken to \"%s\" on host %s",
07995                       _client_connection[i].client_name, _client_connection[i].host_name);
07996 
07997                /* connection broken -> reset */
07998                closesocket(sock);
07999                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08000             }
08001          }
08002       }
08003 }
08004 
08005 
08006 /********************************************************************/
08007 INT rpc_server_connect(char *host_name, char *exp_name)
08008 /********************************************************************\
08009 
08010   Routine: rpc_server_connect
08011 
08012   Purpose: Extablish a network connection to a remote MIDAS
08013            server using a callback scheme.
08014 
08015   Input:
08016     char *host_name         IP address of host to connect to.
08017 
08018     INT  port               TPC port to connect to.
08019 
08020     char *exp_name          Name of experiment to connect to. By using
08021                             this name, several experiments (e.g. online
08022                             DAQ and offline analysis) can run simultan-
08023                             eously on the same host.
08024 
08025   Output:
08026     none
08027 
08028   Function value:
08029     RPC_SUCCESS              Successful completion
08030     RPC_NET_ERROR            Error in socket call
08031     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08032     CM_UNDEF_EXP             Undefined experiment on server
08033 
08034 \********************************************************************/
08035 {
08036    INT i, status, flag;
08037    struct sockaddr_in bind_addr;
08038    INT sock, lsock1, lsock2, lsock3;
08039    INT listen_port1, listen_port2, listen_port3;
08040    INT remote_hw_type, hw_type;
08041    int size;
08042    char str[200], version[32], v1[32];
08043    char local_prog_name[NAME_LENGTH];
08044    struct hostent *phe;
08045 
08046 #ifdef OS_WINNT
08047    {
08048       WSADATA WSAData;
08049 
08050       /* Start windows sockets */
08051       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08052          return RPC_NET_ERROR;
08053    }
08054 #endif
08055 
08056    /* check if local connection */
08057    if (host_name[0] == 0)
08058       return RPC_SUCCESS;
08059 
08060    /* register system functions */
08061    rpc_register_functions(rpc_get_internal_list(0), NULL);
08062 
08063    /* check if cm_connect_experiment was called */
08064    if (_client_name[0] == 0) {
08065       cm_msg(MERROR, "rpc_server_connect",
08066              "cm_connect_experiment/rpc_set_name not called");
08067       return RPC_NOT_REGISTERED;
08068    }
08069 
08070    /* check if connection already exists */
08071    if (_server_connection.send_sock != 0)
08072       return RPC_SUCCESS;
08073 
08074    strcpy(_server_connection.host_name, host_name);
08075    strcpy(_server_connection.exp_name, exp_name);
08076    _server_connection.transport = RPC_TCP;
08077    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08078 
08079    /* create new TCP sockets for listening */
08080    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08081    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08082    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08083    if (lsock3 == -1) {
08084       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08085       return RPC_NET_ERROR;
08086    }
08087 
08088    flag = 1;
08089    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08090    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08091    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08092 
08093    /* let OS choose any port number */
08094    memset(&bind_addr, 0, sizeof(bind_addr));
08095    bind_addr.sin_family = AF_INET;
08096    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08097    bind_addr.sin_port = 0;
08098 
08099    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08100    bind_addr.sin_port = 0;
08101    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08102    bind_addr.sin_port = 0;
08103    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08104    if (status < 0) {
08105       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08106       return RPC_NET_ERROR;
08107    }
08108 
08109    /* listen for connection */
08110    status = listen(lsock1, 1);
08111    status = listen(lsock2, 1);
08112    status = listen(lsock3, 1);
08113    if (status < 0) {
08114       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08115       return RPC_NET_ERROR;
08116    }
08117 
08118    /* find out which port OS has chosen */
08119    size = sizeof(bind_addr);
08120    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08121    listen_port1 = ntohs(bind_addr.sin_port);
08122    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08123    listen_port2 = ntohs(bind_addr.sin_port);
08124    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08125    listen_port3 = ntohs(bind_addr.sin_port);
08126 
08127    /* create a new socket for connecting to remote server */
08128    sock = socket(AF_INET, SOCK_STREAM, 0);
08129    if (sock == -1) {
08130       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08131       return RPC_NET_ERROR;
08132    }
08133 
08134    /* connect to remote node */
08135    memset(&bind_addr, 0, sizeof(bind_addr));
08136    bind_addr.sin_family = AF_INET;
08137    bind_addr.sin_addr.s_addr = 0;
08138    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08139 
08140 #ifdef OS_VXWORKS
08141    {
08142       INT host_addr;
08143 
08144       host_addr = hostGetByName(host_name);
08145       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08146    }
08147 #else
08148    phe = gethostbyname(host_name);
08149    if (phe == NULL) {
08150       cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08151       return RPC_NET_ERROR;
08152    }
08153    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08154 #endif
08155 
08156 #ifdef OS_UNIX
08157    do {
08158       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08159 
08160       /* don't return if an alarm signal was cought */
08161    } while (status == -1 && errno == EINTR);
08162 #else
08163    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08164 #endif
08165 
08166    if (status != 0) {
08167 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08168       return RPC_NET_ERROR;
08169    }
08170 
08171    /* connect to experiment */
08172    if (exp_name[0] == 0)
08173       sprintf(str, "C %d %d %d %s Default",
08174               listen_port1, listen_port2, listen_port3, cm_get_version());
08175    else
08176       sprintf(str, "C %d %d %d %s %s",
08177               listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08178 
08179    send(sock, str, strlen(str) + 1, 0);
08180    i = recv_string(sock, str, sizeof(str), 10000);
08181    closesocket(sock);
08182    if (i <= 0) {
08183       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08184       return RPC_NET_ERROR;
08185    }
08186 
08187    status = version[0] = 0;
08188    sscanf(str, "%d %s", &status, version);
08189 
08190    if (status == 2) {
08191 /*  message "undefined experiment" should be displayed by application */
08192       return CM_UNDEF_EXP;
08193    }
08194 
08195    /* print warning if version patch level doesn't agree */
08196    strcpy(v1, version);
08197    if (strchr(v1, '.'))
08198       if (strchr(strchr(v1, '.') + 1, '.'))
08199          *strchr(strchr(v1, '.') + 1, '.') = 0;
08200 
08201    strcpy(str, cm_get_version());
08202    if (strchr(str, '.'))
08203       if (strchr(strchr(str, '.') + 1, '.'))
08204          *strchr(strchr(str, '.') + 1, '.') = 0;
08205 
08206    if (strcmp(v1, str) != 0) {
08207       sprintf(str, "remote MIDAS version %s differs from local version %s",
08208               version, cm_get_version());
08209       cm_msg(MERROR, "rpc_server_connect", str);
08210    }
08211 
08212    /* wait for callback on send and recv socket */
08213    size = sizeof(bind_addr);
08214    _server_connection.send_sock =
08215        accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08216 
08217    _server_connection.recv_sock =
08218        accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08219 
08220    _server_connection.event_sock =
08221        accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08222 
08223    if (_server_connection.send_sock == -1 ||
08224        _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
08225       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08226       return RPC_NET_ERROR;
08227    }
08228 
08229    closesocket(lsock1);
08230    closesocket(lsock2);
08231    closesocket(lsock3);
08232 
08233    /* set TCP_NODELAY option for better performance */
08234 #ifdef OS_VXWORKS
08235    flag = 1;
08236    setsockopt(_server_connection.send_sock,
08237               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08238    setsockopt(_server_connection.event_sock,
08239               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08240 #endif
08241 
08242    /* increase send buffer size to 64kB */
08243    flag = 0x10000;
08244    setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF,
08245               (char *) &flag, sizeof(flag));
08246 
08247    /* send local computer info */
08248    rpc_get_name(local_prog_name);
08249    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08250    sprintf(str, "%d %s", hw_type, local_prog_name);
08251 
08252    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08253 
08254    /* receive remote computer info */
08255    i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
08256    if (i <= 0) {
08257       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
08258       return RPC_NET_ERROR;
08259    }
08260 
08261    sscanf(str, "%d", &remote_hw_type);
08262    _server_connection.remote_hw_type = remote_hw_type;
08263 
08264    /* set dispatcher which receives database updates */
08265    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection,
08266                            (int (*)(void)) rpc_client_dispatch);
08267 
08268    return RPC_SUCCESS;
08269 }
08270 
08271 
08272 /********************************************************************/
08273 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08274 /********************************************************************\
08275 
08276   Routine: rpc_client_disconnect
08277 
08278   Purpose: Close a rpc connection to a MIDAS client
08279 
08280   Input:
08281     HNDLE  hConn           Handle of connection
08282     BOOL   bShutdown       Shut down remote server if TRUE
08283 
08284   Output:
08285     none
08286 
08287   Function value:
08288    RPC_SUCCESS             Successful completion
08289 
08290 \********************************************************************/
08291 {
08292    INT i;
08293 
08294    if (hConn == -1) {
08295       /* close all open connections */
08296       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
08297          if (_client_connection[i].send_sock != 0)
08298             rpc_client_disconnect(i + 1, FALSE);
08299 
08300       /* close server connection from other clients */
08301       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08302          if (_server_acception[i].recv_sock) {
08303             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
08304             closesocket(_server_acception[i].recv_sock);
08305          }
08306    } else {
08307       /* notify server about exit */
08308 
08309       /* set FTCP mode (helps for rebooted VxWorks nodes) */
08310       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
08311       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
08312 
08313       /* close socket */
08314       if (_client_connection[hConn - 1].send_sock)
08315          closesocket(_client_connection[hConn - 1].send_sock);
08316 
08317       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
08318    }
08319 
08320    return RPC_SUCCESS;
08321 }
08322 
08323 
08324 /********************************************************************/
08325 INT rpc_server_disconnect()
08326 /********************************************************************\
08327 
08328   Routine: rpc_server_disconnect
08329 
08330   Purpose: Close a rpc connection to a MIDAS server and close all
08331            server connections from other clients
08332 
08333   Input:
08334     none
08335 
08336   Output:
08337     none
08338 
08339   Function value:
08340    RPC_SUCCESS             Successful completion
08341    RPC_NET_ERROR           Error in socket call
08342    RPC_NO_CONNECTION       Maximum number of connections reached
08343 
08344 \********************************************************************/
08345 {
08346    static int rpc_server_disconnect_recursion_level = 0;
08347 
08348    if (rpc_server_disconnect_recursion_level)
08349       return RPC_SUCCESS;
08350 
08351    rpc_server_disconnect_recursion_level = 1;
08352 
08353    /* flush remaining events */
08354    rpc_flush_event();
08355 
08356    /* notify server about exit */
08357    rpc_call(RPC_ID_EXIT);
08358 
08359    /* close sockets */
08360    closesocket(_server_connection.send_sock);
08361    closesocket(_server_connection.recv_sock);
08362    closesocket(_server_connection.event_sock);
08363 
08364    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
08365 
08366    rpc_server_disconnect_recursion_level = 0;
08367    return RPC_SUCCESS;
08368 }
08369 
08370 
08371 /********************************************************************/
08372 INT rpc_is_remote(void)
08373 /********************************************************************\
08374 
08375   Routine: rpc_is_remote
08376 
08377   Purpose: Return true if program is connected to a remote server
08378 
08379   Input:
08380    none
08381 
08382   Output:
08383     none
08384 
08385   Function value:
08386     INT    RPC connection index
08387 
08388 \********************************************************************/
08389 {
08390    return _server_connection.send_sock != 0;
08391 }
08392 
08393 
08394 /********************************************************************/
08395 INT rpc_get_server_acception(void)
08396 /********************************************************************\
08397 
08398   Routine: rpc_get_server_acception
08399 
08400   Purpose: Return actual RPC server connection index
08401 
08402   Input:
08403    none
08404 
08405   Output:
08406     none
08407 
08408   Function value:
08409     INT    RPC server connection index
08410 
08411 \********************************************************************/
08412 {
08413    return _server_acception_index;
08414 }
08415 
08416 
08417 /********************************************************************/
08418 INT rpc_set_server_acception(INT index)
08419 /********************************************************************\
08420 
08421   Routine: rpc_set_server_acception
08422 
08423   Purpose: Set actual RPC server connection index
08424 
08425   Input:
08426     INT  index              Server index
08427 
08428   Output:
08429     none
08430 
08431   Function value:
08432     RPC_SUCCESS             Successful completion
08433 
08434 \********************************************************************/
08435 {
08436    _server_acception_index = index;
08437    return RPC_SUCCESS;
08438 }
08439 
08440 
08441 /********************************************************************/
08442 INT rpc_get_option(HNDLE hConn, INT item)
08443 /********************************************************************\
08444 
08445   Routine: rpc_get_option
08446 
08447   Purpose: Get actual RPC option
08448 
08449   Input:
08450     HNDLE hConn             RPC connection handle
08451     INT   item              One of RPC_Oxxx
08452 
08453   Output:
08454     none
08455 
08456   Function value:
08457     INT                     Actual option
08458 
08459 \********************************************************************/
08460 {
08461    switch (item) {
08462    case RPC_OTIMEOUT:
08463       if (hConn == -1)
08464          return _server_connection.rpc_timeout;
08465       return _client_connection[hConn - 1].rpc_timeout;
08466 
08467    case RPC_OTRANSPORT:
08468       if (hConn == -1)
08469          return _server_connection.transport;
08470       return _client_connection[hConn - 1].transport;
08471 
08472    case RPC_OHW_TYPE:
08473       {
08474          INT tmp_type, size;
08475          DWORD dummy;
08476          unsigned char *p;
08477          float f;
08478          double d;
08479 
08480          tmp_type = 0;
08481 
08482          /* test pointer size */
08483          size = sizeof(p);
08484          if (size == 2)
08485             tmp_type |= DRI_16;
08486          if (size == 4)
08487             tmp_type |= DRI_32;
08488          if (size == 8)
08489             tmp_type |= DRI_64;
08490 
08491          /* test if little or big endian machine */
08492          dummy = 0x12345678;
08493          p = (unsigned char *) &dummy;
08494          if (*p == 0x78)
08495             tmp_type |= DRI_LITTLE_ENDIAN;
08496          else if (*p == 0x12)
08497             tmp_type |= DRI_BIG_ENDIAN;
08498          else
08499             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
08500 
08501          /* floating point format */
08502          f = (float) 1.2345;
08503          dummy = 0;
08504          memcpy(&dummy, &f, sizeof(f));
08505          if ((dummy & 0xFF) == 0x19 &&
08506              ((dummy >> 8) & 0xFF) == 0x04 &&
08507              ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
08508             tmp_type |= DRF_IEEE;
08509          else if ((dummy & 0xFF) == 0x9E &&
08510                   ((dummy >> 8) & 0xFF) == 0x40 &&
08511                   ((dummy >> 16) & 0xFF) == 0x19 && ((dummy >> 24) & 0xFF) == 0x04)
08512             tmp_type |= DRF_G_FLOAT;
08513          else
08514             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08515 
08516          d = (double) 1.2345;
08517          dummy = 0;
08518          memcpy(&dummy, &d, sizeof(f));
08519          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
08520              ((dummy >> 8) & 0xFF) == 0x97 &&
08521              ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
08522             tmp_type |= DRF_IEEE;
08523          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
08524                   ((dummy >> 8) & 0xFF) == 0xC0 &&
08525                   ((dummy >> 16) & 0xFF) == 0xF3 && ((dummy >> 24) & 0xFF) == 0x3F)
08526             tmp_type |= DRF_IEEE;
08527          else if ((dummy & 0xFF) == 0x13 &&
08528                   ((dummy >> 8) & 0xFF) == 0x40 &&
08529                   ((dummy >> 16) & 0xFF) == 0x83 && ((dummy >> 24) & 0xFF) == 0xC0)
08530             tmp_type |= DRF_G_FLOAT;
08531          else if ((dummy & 0xFF) == 0x9E &&
08532                   ((dummy >> 8) & 0xFF) == 0x40 &&
08533                   ((dummy >> 16) & 0xFF) == 0x18 && ((dummy >> 24) & 0xFF) == 0x04)
08534             cm_msg(MERROR, "rpc_get_option",
08535                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
08536          else
08537             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08538 
08539          return tmp_type;
08540       }
08541 
08542    default:
08543       cm_msg(MERROR, "rpc_get_option", "invalid argument");
08544       break;
08545    }
08546 
08547    return 0;
08548 }
08549 
08550 /**dox***************************************************************/
08551 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08552 
08553 /********************************************************************/
08554 /**
08555 Set RPC option
08556 @param hConn              RPC connection handle
08557 @param item               One of RPC_Oxxx
08558 @param value              Value to set
08559 @return RPC_SUCCESS
08560 */
08561 INT rpc_set_option(HNDLE hConn, INT item, INT value)
08562 {
08563    switch (item) {
08564    case RPC_OTIMEOUT:
08565       if (hConn == -1)
08566          _server_connection.rpc_timeout = value;
08567       else
08568          _client_connection[hConn - 1].rpc_timeout = value;
08569       break;
08570 
08571    case RPC_OTRANSPORT:
08572       if (hConn == -1)
08573          _server_connection.transport = value;
08574       else
08575          _client_connection[hConn - 1].transport = value;
08576       break;
08577 
08578    case RPC_NODELAY:
08579       if (hConn == -1)
08580          setsockopt(_server_connection.send_sock, IPPROTO_TCP,
08581                     TCP_NODELAY, (char *) &value, sizeof(value));
08582       else
08583          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP,
08584                     TCP_NODELAY, (char *) &value, sizeof(value));
08585       break;
08586 
08587    default:
08588       cm_msg(MERROR, "rpc_set_option", "invalid argument");
08589       break;
08590    }
08591 
08592    return 0;
08593 }
08594 
08595 
08596 /**dox***************************************************************/
08597 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08598 
08599 /********************************************************************/
08600 PTYPE rpc_get_server_option(INT item)
08601 /********************************************************************\
08602 
08603   Routine: rpc_get_server_option
08604 
08605   Purpose: Get actual RPC option for server connection
08606 
08607   Input:
08608     INT  item               One of RPC_Oxxx
08609 
08610   Output:
08611     none
08612 
08613   Function value:
08614     INT                     Actual option
08615 
08616 \********************************************************************/
08617 {
08618    INT i;
08619 
08620    if (item == RPC_OSERVER_TYPE)
08621       return _server_type;
08622 
08623    if (item == RPC_OSERVER_NAME)
08624       return (PTYPE) _server_name;
08625 
08626    /* return 0 for local calls */
08627    if (_server_type == ST_NONE)
08628       return 0;
08629 
08630    /* check which connections belongs to caller */
08631    if (_server_type == ST_MTHREAD) {
08632       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08633          if (_server_acception[i].tid == ss_gettid())
08634             break;
08635    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08636       i = MAX(0, _server_acception_index - 1);
08637    else
08638       i = 0;
08639 
08640    switch (item) {
08641    case RPC_CONVERT_FLAGS:
08642       return _server_acception[i].convert_flags;
08643    case RPC_ODB_HANDLE:
08644       return _server_acception[i].odb_handle;
08645    case RPC_CLIENT_HANDLE:
08646       return _server_acception[i].client_handle;
08647    case RPC_SEND_SOCK:
08648       return _server_acception[i].send_sock;
08649    case RPC_WATCHDOG_TIMEOUT:
08650       return _server_acception[i].watchdog_timeout;
08651    }
08652 
08653    return 0;
08654 }
08655 
08656 
08657 /********************************************************************/
08658 INT rpc_set_server_option(INT item, PTYPE value)
08659 /********************************************************************\
08660 
08661   Routine: rpc_set_server_option
08662 
08663   Purpose: Set RPC option for server connection
08664 
08665   Input:
08666    INT  item               One of RPC_Oxxx
08667    INT  value              Value to set
08668 
08669   Output:
08670     none
08671 
08672   Function value:
08673     RPC_SUCCESS             Successful completion
08674 
08675 \********************************************************************/
08676 {
08677    INT i;
08678 
08679    if (item == RPC_OSERVER_TYPE) {
08680       _server_type = value;
08681       return RPC_SUCCESS;
08682    }
08683    if (item == RPC_OSERVER_NAME) {
08684       strcpy(_server_name, (char *) value);
08685       return RPC_SUCCESS;
08686    }
08687 
08688    /* check which connections belongs to caller */
08689    if (_server_type == ST_MTHREAD) {
08690       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08691          if (_server_acception[i].tid == ss_gettid())
08692             break;
08693    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08694       i = MAX(0, _server_acception_index - 1);
08695    else
08696       i = 0;
08697 
08698    switch (item) {
08699    case RPC_CONVERT_FLAGS:
08700       _server_acception[i].convert_flags = value;
08701       break;
08702    case RPC_ODB_HANDLE:
08703       _server_acception[i].odb_handle = value;
08704       break;
08705    case RPC_CLIENT_HANDLE:
08706       _server_acception[i].client_handle = value;
08707       break;
08708    case RPC_WATCHDOG_TIMEOUT:
08709       _server_acception[i].watchdog_timeout = value;
08710       break;
08711    }
08712 
08713    return RPC_SUCCESS;
08714 }
08715 
08716 
08717 /********************************************************************/
08718 INT rpc_get_name(char *name)
08719 /********************************************************************\
08720 
08721   Routine: rpc_get_name
08722 
08723   Purpose: Get name set by rpc_set_name
08724 
08725   Input:
08726     none
08727 
08728   Output:
08729     char*  name             The location pointed by *name receives a
08730                             copy of the _prog_name
08731 
08732   Function value:
08733     RPC_SUCCESS             Successful completion
08734 
08735 \********************************************************************/
08736 {
08737    strcpy(name, _client_name);
08738 
08739    return RPC_SUCCESS;
08740 }
08741 
08742 
08743 /********************************************************************/
08744 INT rpc_set_name(char *name)
08745 /********************************************************************\
08746 
08747   Routine: rpc_set_name
08748 
08749   Purpose: Set name of actual program for further rpc connections
08750 
08751   Input:
08752    char *name               Program name, up to NAME_LENGTH chars,
08753                             no blanks
08754 
08755   Output:
08756     none
08757 
08758   Function value:
08759     RPC_SUCCESS             Successful completion
08760 
08761 \********************************************************************/
08762 {
08763    strcpy(_client_name, name);
08764 
08765    return RPC_SUCCESS;
08766 }
08767 
08768 
08769 /********************************************************************/
08770 INT rpc_set_debug(void (*func) (char *), INT mode)
08771 /********************************************************************\
08772 
08773   Routine: rpc_set_debug
08774 
08775   Purpose: Set a function which is called on every RPC call to
08776            display the function name and parameters of the RPC
08777            call.
08778 
08779   Input:
08780    void *func(char*)        Pointer to function.
08781    INT  mode                Debug mode
08782 
08783   Output:
08784     none
08785 
08786   Function value:
08787     RPC_SUCCESS             Successful completion
08788 
08789 \********************************************************************/
08790 {
08791    _debug_print = func;
08792    _debug_mode = mode;
08793    return RPC_SUCCESS;
08794 }
08795 
08796 /********************************************************************/
08797 void rpc_debug_printf(char *format, ...)
08798 /********************************************************************\
08799 
08800   Routine: rpc_debug_print
08801 
08802   Purpose: Calls function set via rpc_set_debug to output a string.
08803 
08804   Input:
08805    char *str                Debug string
08806 
08807   Output:
08808     none
08809 
08810 \********************************************************************/
08811 {
08812    va_list argptr;
08813    char str[1000];
08814 
08815    if (_debug_mode) {
08816       va_start(argptr, format);
08817       vsprintf(str, (char *) format, argptr);
08818       va_end(argptr);
08819 
08820       if (_debug_print) {
08821          strcat(str, "\n");
08822          _debug_print(str);
08823       } else
08824          puts(str);
08825    }
08826 }
08827 
08828 /********************************************************************/
08829 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
08830 {
08831    switch (arg_type) {
08832       /* On the stack, the minimum parameter size is sizeof(int).
08833          To avoid problems on little endian systems, treat all
08834          smaller parameters as int's */
08835    case TID_BYTE:
08836    case TID_SBYTE:
08837    case TID_CHAR:
08838    case TID_WORD:
08839    case TID_SHORT:
08840       *((int *) arg) = va_arg(*arg_ptr, int);
08841       break;
08842 
08843    case TID_INT:
08844    case TID_BOOL:
08845       *((INT *) arg) = va_arg(*arg_ptr, INT);
08846       break;
08847 
08848    case TID_DWORD:
08849       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
08850       break;
08851 
08852       /* float variables are passed as double by the compiler */
08853    case TID_FLOAT:
08854       *((float *) arg) = (float) va_arg(*arg_ptr, double);
08855       break;
08856 
08857    case TID_DOUBLE:
08858       *((double *) arg) = va_arg(*arg_ptr, double);
08859       break;
08860 
08861    case TID_ARRAY:
08862       *((char **) arg) = va_arg(*arg_ptr, char *);
08863       break;
08864    }
08865 }
08866 
08867 
08868 /********************************************************************/
08869 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
08870 /********************************************************************\
08871 
08872   Routine: rpc_client_call
08873 
08874   Purpose: Call a function on a MIDAS client
08875 
08876   Input:
08877     INT  hConn              Client connection
08878     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
08879 
08880     ...                     variable argument list
08881 
08882   Output:
08883     (depends on argument list)
08884 
08885   Function value:
08886     RPC_SUCCESS             Successful completion
08887     RPC_NET_ERROR           Error in socket call
08888     RPC_NO_CONNECTION       No active connection
08889     RPC_TIMEOUT             Timeout in RPC call
08890     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
08891     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
08892 
08893 \********************************************************************/
08894 {
08895    va_list ap, aptmp;
08896    char arg[8], arg_tmp[8];
08897    INT arg_type, transport, rpc_timeout;
08898    INT i, index, status, rpc_index;
08899    INT param_size, arg_size, send_size;
08900    INT tid, flags;
08901    fd_set readfds;
08902    struct timeval timeout;
08903    char *param_ptr, str[80];
08904    BOOL bpointer, bbig;
08905    NET_COMMAND *nc;
08906    int send_sock;
08907 
08908    index = hConn - 1;
08909 
08910    if (_client_connection[index].send_sock == 0) {
08911       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
08912       return RPC_NO_CONNECTION;
08913    }
08914 
08915    send_sock = _client_connection[index].send_sock;
08916    rpc_timeout = _client_connection[index].rpc_timeout;
08917    transport = _client_connection[index].transport;
08918 
08919    /* init network buffer */
08920    if (_net_send_buffer_size == 0) {
08921       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
08922       if (_net_send_buffer == NULL) {
08923          cm_msg(MERROR, "rpc_client_call",
08924                 "not enough memory to allocate network buffer");
08925          return RPC_EXCEED_BUFFER;
08926       }
08927       _net_send_buffer_size = NET_BUFFER_SIZE;
08928    }
08929 
08930    nc = (NET_COMMAND *) _net_send_buffer;
08931    nc->header.routine_id = routine_id;
08932 
08933    if (transport == RPC_FTCP)
08934       nc->header.routine_id |= TCP_FAST;
08935 
08936    for (i = 0;; i++)
08937       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
08938          break;
08939    rpc_index = i;
08940    if (rpc_list[i].id == 0) {
08941       sprintf(str, "invalid rpc ID (%d)", routine_id);
08942       cm_msg(MERROR, "rpc_client_call", str);
08943       return RPC_INVALID_ID;
08944    }
08945 
08946    /* examine variable argument list and convert it to parameter array */
08947    va_start(ap, routine_id);
08948 
08949    /* find out if we are on a big endian system */
08950    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
08951 
08952    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
08953       tid = rpc_list[rpc_index].param[i].tid;
08954       flags = rpc_list[rpc_index].param[i].flags;
08955 
08956       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
08957           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
08958           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
08959 
08960       if (bpointer)
08961          arg_type = TID_ARRAY;
08962       else
08963          arg_type = tid;
08964 
08965       /* floats are passed as doubles, at least under NT */
08966       if (tid == TID_FLOAT && !bpointer)
08967          arg_type = TID_DOUBLE;
08968 
08969       /* get pointer to argument */
08970       rpc_va_arg(&ap, arg_type, arg);
08971 
08972       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
08973       if (bbig) {
08974          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
08975             arg[0] = arg[3];
08976          }
08977          if (tid == TID_WORD || tid == TID_SHORT) {
08978             arg[0] = arg[2];
08979             arg[1] = arg[3];
08980          }
08981       }
08982 
08983       if (flags & RPC_IN) {
08984          if (bpointer)
08985             arg_size = tid_size[tid];
08986          else
08987             arg_size = tid_size[arg_type];
08988 
08989          /* for strings, the argument size depends on the string length */
08990          if (tid == TID_STRING || tid == TID_LINK)
08991             arg_size = 1 + strlen((char *) *((char **) arg));
08992 
08993          /* for varibale length arrays, the size is given by
08994             the next parameter on the stack */
08995          if (flags & RPC_VARARRAY) {
08996             memcpy(&aptmp, &ap, sizeof(ap));
08997             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
08998 
08999             if (flags & RPC_OUT)
09000                arg_size = *((INT *) * ((void **) arg_tmp));
09001             else
09002                arg_size = *((INT *) arg_tmp);
09003 
09004             *((INT *) param_ptr) = ALIGN8(arg_size);
09005             param_ptr += ALIGN8(sizeof(INT));
09006          }
09007 
09008          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09009             arg_size = rpc_list[rpc_index].param[i].n;
09010 
09011          /* always align parameter size */
09012          param_size = ALIGN8(arg_size);
09013 
09014          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09015             cm_msg(MERROR, "rpc_client_call",
09016                    "parameters (%d) too large for network buffer (%d)",
09017                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
09018             return RPC_EXCEED_BUFFER;
09019          }
09020 
09021          if (bpointer)
09022             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09023          else {
09024             /* floats are passed as doubles on most systems */
09025             if (tid != TID_FLOAT)
09026                memcpy(param_ptr, arg, arg_size);
09027             else
09028                *((float *) param_ptr) = (float) *((double *) arg);
09029          }
09030 
09031          param_ptr += param_size;
09032 
09033       }
09034    }
09035 
09036    va_end(ap);
09037 
09038    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09039 
09040    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09041 
09042    /* in FAST TCP mode, only send call and return immediately */
09043    if (transport == RPC_FTCP) {
09044       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09045 
09046       if (i != send_size) {
09047          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09048          return RPC_NET_ERROR;
09049       }
09050 
09051       return RPC_SUCCESS;
09052    }
09053 
09054    /* in TCP mode, send and wait for reply on send socket */
09055    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09056    if (i != send_size) {
09057       cm_msg(MERROR, "rpc_client_call",
09058              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09059              rpc_list[rpc_index].name, _client_connection[index].host_name);
09060       return RPC_NET_ERROR;
09061    }
09062 
09063    /* make some timeout checking */
09064    if (rpc_timeout > 0) {
09065       FD_ZERO(&readfds);
09066       FD_SET(send_sock, &readfds);
09067 
09068       timeout.tv_sec = rpc_timeout / 1000;
09069       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09070 
09071       do {
09072          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09073 
09074          /* if an alarm signal was cought, restart select with reduced timeout */
09075          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09076             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09077 
09078       } while (status == -1);   /* dont return if an alarm signal was cought */
09079 
09080       if (!FD_ISSET(send_sock, &readfds)) {
09081          cm_msg(MERROR, "rpc_client_call", "rpc timeout, routine = \"%s\", host = \"%s\"",
09082                 rpc_list[rpc_index].name, _client_connection[index].host_name);
09083 
09084          /* disconnect to avoid that the reply to this rpc_call comes at
09085             the next rpc_call */
09086          rpc_client_disconnect(hConn, FALSE);
09087 
09088          return RPC_TIMEOUT;
09089       }
09090    }
09091 
09092    /* receive result on send socket */
09093    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09094 
09095    if (i <= 0) {
09096       cm_msg(MERROR, "rpc_client_call",
09097              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09098              rpc_list[rpc_index].name, _client_connection[index].host_name);
09099       return RPC_NET_ERROR;
09100    }
09101 
09102    /* extract result variables and place it to argument list */
09103    status = nc->header.routine_id;
09104 
09105    va_start(ap, routine_id);
09106 
09107    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09108       tid = rpc_list[rpc_index].param[i].tid;
09109       flags = rpc_list[rpc_index].param[i].flags;
09110 
09111       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09112           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09113           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09114 
09115       if (bpointer)
09116          arg_type = TID_ARRAY;
09117       else
09118          arg_type = rpc_list[rpc_index].param[i].tid;
09119 
09120       if (tid == TID_FLOAT && !bpointer)
09121          arg_type = TID_DOUBLE;
09122 
09123       rpc_va_arg(&ap, arg_type, arg);
09124 
09125       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09126          tid = rpc_list[rpc_index].param[i].tid;
09127          flags = rpc_list[rpc_index].param[i].flags;
09128 
09129          arg_size = tid_size[tid];
09130 
09131          if (tid == TID_STRING || tid == TID_LINK)
09132             arg_size = strlen((char *) (param_ptr)) + 1;
09133 
09134          if (flags & RPC_VARARRAY) {
09135             arg_size = *((INT *) param_ptr);
09136             param_ptr += ALIGN8(sizeof(INT));
09137          }
09138 
09139          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09140             arg_size = rpc_list[rpc_index].param[i].n;
09141 
09142          /* return parameters are always pointers */
09143          if (*((char **) arg))
09144             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09145 
09146          /* parameter size is always aligned */
09147          param_size = ALIGN8(arg_size);
09148 
09149          param_ptr += param_size;
09150       }
09151    }
09152 
09153    va_end(ap);
09154 
09155    return status;
09156 }
09157 
09158 
09159 /********************************************************************/
09160 INT rpc_call(const INT routine_id, ...)
09161 /********************************************************************\
09162 
09163   Routine: rpc_call
09164 
09165   Purpose: Call a function on a MIDAS server
09166 
09167   Input:
09168     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09169 
09170     ...                     variable argument list
09171 
09172   Output:
09173     (depends on argument list)
09174 
09175   Function value:
09176     RPC_SUCCESS             Successful completion
09177     RPC_NET_ERROR           Error in socket call
09178     RPC_NO_CONNECTION       No active connection
09179     RPC_TIMEOUT             Timeout in RPC call
09180     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09181     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09182 
09183 \********************************************************************/
09184 {
09185    va_list ap, aptmp;
09186    char arg[8], arg_tmp[8];
09187    INT arg_type, transport, rpc_timeout;
09188    INT i, index, status;
09189    INT param_size, arg_size, send_size;
09190    INT tid, flags;
09191    fd_set readfds;
09192    struct timeval timeout;
09193    char *param_ptr, str[80];
09194    BOOL bpointer, bbig;
09195    NET_COMMAND *nc;
09196    int send_sock;
09197 
09198    send_sock = _server_connection.send_sock;
09199    transport = _server_connection.transport;
09200    rpc_timeout = _server_connection.rpc_timeout;
09201 
09202    /* init network buffer */
09203    if (_net_send_buffer_size == 0) {
09204       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09205       if (_net_send_buffer == NULL) {
09206          cm_msg(MERROR, "rpc_call", "not enough memory to allocate network buffer");
09207          return RPC_EXCEED_BUFFER;
09208       }
09209       _net_send_buffer_size = NET_BUFFER_SIZE;
09210    }
09211 
09212    nc = (NET_COMMAND *) _net_send_buffer;
09213    nc->header.routine_id = routine_id;
09214 
09215    if (transport == RPC_FTCP)
09216       nc->header.routine_id |= TCP_FAST;
09217 
09218    for (i = 0;; i++)
09219       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09220          break;
09221    index = i;
09222    if (rpc_list[i].id == 0) {
09223       sprintf(str, "invalid rpc ID (%d)", routine_id);
09224       cm_msg(MERROR, "rpc_call", str);
09225       return RPC_INVALID_ID;
09226    }
09227 
09228    /* examine variable argument list and convert it to parameter array */
09229    va_start(ap, routine_id);
09230 
09231    /* find out if we are on a big endian system */
09232    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09233 
09234    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09235       tid = rpc_list[index].param[i].tid;
09236       flags = rpc_list[index].param[i].flags;
09237 
09238       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09239           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09240           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09241 
09242       if (bpointer)
09243          arg_type = TID_ARRAY;
09244       else
09245          arg_type = tid;
09246 
09247       /* floats are passed as doubles, at least under NT */
09248       if (tid == TID_FLOAT && !bpointer)
09249          arg_type = TID_DOUBLE;
09250 
09251       /* get pointer to argument */
09252       rpc_va_arg(&ap, arg_type, arg);
09253 
09254       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09255       if (bbig) {
09256          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09257             arg[0] = arg[3];
09258          }
09259          if (tid == TID_WORD || tid == TID_SHORT) {
09260             arg[0] = arg[2];
09261             arg[1] = arg[3];
09262          }
09263       }
09264 
09265       if (flags & RPC_IN) {
09266          if (bpointer)
09267             arg_size = tid_size[tid];
09268          else
09269             arg_size = tid_size[arg_type];
09270 
09271          /* for strings, the argument size depends on the string length */
09272          if (tid == TID_STRING || tid == TID_LINK)
09273             arg_size = 1 + strlen((char *) *((char **) arg));
09274 
09275          /* for varibale length arrays, the size is given by
09276             the next parameter on the stack */
09277          if (flags & RPC_VARARRAY) {
09278             memcpy(&aptmp, &ap, sizeof(ap));
09279             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09280 
09281             if (flags & RPC_OUT)
09282                arg_size = *((INT *) * ((void **) arg_tmp));
09283             else
09284                arg_size = *((INT *) arg_tmp);
09285 
09286             *((INT *) param_ptr) = ALIGN8(arg_size);
09287             param_ptr += ALIGN8(sizeof(INT));
09288          }
09289 
09290          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09291             arg_size = rpc_list[index].param[i].n;
09292 
09293          /* always align parameter size */
09294          param_size = ALIGN8(arg_size);
09295 
09296          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09297             cm_msg(MERROR, "rpc_call",
09298                    "parameters (%d) too large for network buffer (%d)",
09299                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
09300             return RPC_EXCEED_BUFFER;
09301          }
09302 
09303          if (bpointer)
09304             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09305          else {
09306             /* floats are passed as doubles on most systems */
09307             if (tid != TID_FLOAT)
09308                memcpy(param_ptr, arg, arg_size);
09309             else
09310                *((float *) param_ptr) = (float) *((double *) arg);
09311          }
09312 
09313          param_ptr += param_size;
09314 
09315       }
09316    }
09317 
09318    va_end(ap);
09319 
09320    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09321 
09322    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09323 
09324    /* in FAST TCP mode, only send call and return immediately */
09325    if (transport == RPC_FTCP) {
09326       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09327 
09328       if (i != send_size) {
09329          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09330          return RPC_NET_ERROR;
09331       }
09332 
09333       return RPC_SUCCESS;
09334    }
09335 
09336    /* in TCP mode, send and wait for reply on send socket */
09337    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09338    if (i != send_size) {
09339       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09340       return RPC_NET_ERROR;
09341    }
09342 
09343    /* make some timeout checking */
09344    if (rpc_timeout > 0) {
09345       FD_ZERO(&readfds);
09346       FD_SET(send_sock, &readfds);
09347 
09348       timeout.tv_sec = rpc_timeout / 1000;
09349       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09350 
09351       do {
09352          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09353 
09354          /* if an alarm signal was cought, restart select with reduced timeout */
09355          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09356             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09357 
09358       } while (status == -1);   /* dont return if an alarm signal was cought */
09359 
09360       if (!FD_ISSET(send_sock, &readfds)) {
09361          cm_msg(MERROR, "rpc_call", "rpc timeout, routine = \"%s\"",
09362                 rpc_list[index].name);
09363 
09364          /* disconnect to avoid that the reply to this rpc_call comes at
09365             the next rpc_call */
09366          rpc_server_disconnect();
09367 
09368          return RPC_TIMEOUT;
09369       }
09370    }
09371 
09372    /* receive result on send socket */
09373    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09374 
09375    if (i <= 0) {
09376       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"",
09377              rpc_list[index].name);
09378       return RPC_NET_ERROR;
09379    }
09380 
09381    /* extract result variables and place it to argument list */
09382    status = nc->header.routine_id;
09383 
09384    va_start(ap, routine_id);
09385 
09386    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09387       tid = rpc_list[index].param[i].tid;
09388       flags = rpc_list[index].param[i].flags;
09389 
09390       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09391           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09392           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09393 
09394       if (bpointer)
09395          arg_type = TID_ARRAY;
09396       else
09397          arg_type = rpc_list[index].param[i].tid;
09398 
09399       if (tid == TID_FLOAT && !bpointer)
09400          arg_type = TID_DOUBLE;
09401 
09402       rpc_va_arg(&ap, arg_type, arg);
09403 
09404       if (rpc_list[index].param[i].flags & RPC_OUT) {
09405          tid = rpc_list[index].param[i].tid;
09406          arg_size = tid_size[tid];
09407 
09408          if (tid == TID_STRING || tid == TID_LINK)
09409             arg_size = strlen((char *) (param_ptr)) + 1;
09410 
09411          if (flags & RPC_VARARRAY) {
09412             arg_size = *((INT *) param_ptr);
09413             param_ptr += ALIGN8(sizeof(INT));
09414          }
09415 
09416          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09417             arg_size = rpc_list[index].param[i].n;
09418 
09419          /* return parameters are always pointers */
09420          if (*((char **) arg))
09421             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09422 
09423          /* parameter size is always aligned */
09424          param_size = ALIGN8(arg_size);
09425 
09426          param_ptr += param_size;
09427       }
09428    }
09429 
09430    va_end(ap);
09431 
09432    return status;
09433 }
09434 
09435 
09436 /********************************************************************/
09437 INT rpc_set_opt_tcp_size(INT tcp_size)
09438 {
09439    INT old;
09440 
09441    old = _opt_tcp_size;
09442    _opt_tcp_size = tcp_size;
09443    return old;
09444 }
09445 
09446 INT rpc_get_opt_tcp_size()
09447 {
09448    return _opt_tcp_size;
09449 }
09450 
09451 /**dox***************************************************************/
09452 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09453 
09454 /********************************************************************/
09455 /**
09456 Fast send_event routine which bypasses the RPC layer and
09457            sends the event directly at the TCP level.
09458 @param buffer_handle      Handle of the buffer to send the event to.
09459                             Must be obtained via bm_open_buffer.
09460 @param source            Address of the event to send. It must have
09461                             a proper event header.
09462 @param buf_size           Size of event in bytes with header.
09463 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
09464                             function returns immediately if it cannot
09465                             send the event over the network. In SYNC
09466                             mode, it waits until the packet is sent
09467                             (blocking).
09468 
09469 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR, 
09470         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER       
09471 */
09472 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
09473 {
09474    INT i;
09475    NET_COMMAND *nc;
09476    unsigned long flag;
09477    BOOL would_block = 0;
09478    DWORD aligned_buf_size;
09479 
09480    aligned_buf_size = ALIGN8(buf_size);
09481 
09482    if (aligned_buf_size !=
09483        (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
09484       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
09485       return BM_INVALID_PARAM;
09486    }
09487    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
09488       cm_msg(MERROR, "rpc_send_event",
09489              "event size (%d) larger than maximum event size (%d)",
09490              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
09491       return RPC_EXCEED_BUFFER;
09492    }
09493 
09494    if (!rpc_is_remote())
09495       return bm_send_event(buffer_handle, source, buf_size, async_flag);
09496 
09497    /* init network buffer */
09498    if (!_tcp_buffer)
09499       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
09500    if (!_tcp_buffer) {
09501       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
09502       return RPC_EXCEED_BUFFER;
09503    }
09504 
09505    /* check if not enough space in TCP buffer */
09506    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
09507        (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
09508       /* set socket to nonblocking IO */
09509       if (async_flag == ASYNC) {
09510          flag = 1;
09511 #ifdef OS_VXWORKS
09512          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
09513 #else
09514          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
09515 #endif
09516       }
09517 
09518       i = send_tcp(_server_connection.send_sock,
09519                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09520 
09521       if (i < 0)
09522 #ifdef OS_WINNT
09523          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
09524 #else
09525          would_block = (errno == EWOULDBLOCK);
09526 #endif
09527 
09528       /* set socket back to blocking IO */
09529       if (async_flag == ASYNC) {
09530          flag = 0;
09531 #ifdef OS_VXWORKS
09532          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
09533 #else
09534          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
09535 #endif
09536       }
09537 
09538       /* increment read pointer */
09539       if (i > 0)
09540          _tcp_rp += i;
09541 
09542       /* check if whole buffer is sent */
09543       if (_tcp_rp == _tcp_wp)
09544          _tcp_rp = _tcp_wp = 0;
09545 
09546       if (i < 0 && !would_block) {
09547          printf("send_tcp() returned %d\n", i);
09548          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed");
09549          return RPC_NET_ERROR;
09550       }
09551 
09552       /* return if buffer is not emptied */
09553       if (_tcp_wp > 0)
09554          return BM_ASYNC_RETURN;
09555    }
09556 
09557    nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
09558    nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
09559    nc->header.param_size = 4 * 8 + aligned_buf_size;
09560 
09561    /* assemble parameters manually */
09562    *((INT *) (&nc->param[0])) = buffer_handle;
09563    *((INT *) (&nc->param[8])) = buf_size;
09564 
09565    /* send events larger than optimal buffer size directly */
09566    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
09567       /* send header */
09568       send_tcp(_server_connection.send_sock,
09569                _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
09570 
09571       /* send data */
09572       send_tcp(_server_connection.send_sock, (char *) source, aligned_buf_size, 0);
09573 
09574       /* send last two parameters */
09575       *((INT *) (&nc->param[0])) = buf_size;
09576       *((INT *) (&nc->param[8])) = 0;
09577       send_tcp(_server_connection.send_sock, &nc->param[0], 16, 0);
09578    } else {
09579       /* copy event */
09580       memcpy(&nc->param[16], source, buf_size);
09581 
09582       /* last two parameters (buf_size and async_flag */
09583       *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
09584       *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
09585 
09586       _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09587    }
09588 
09589    return RPC_SUCCESS;
09590 }
09591 
09592 
09593 
09594 /**dox***************************************************************/
09595 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09596 
09597 /********************************************************************/
09598 int rpc_get_send_sock()
09599 /********************************************************************\
09600 
09601   Routine: rpc_get_send_sock
09602 
09603   Purpose: Return send socket to MIDAS server. Used by MFE.C for
09604            optimized event sending.
09605 
09606   Input:
09607     none
09608 
09609   Output:
09610     none
09611 
09612   Function value:
09613     int    socket
09614 
09615 \********************************************************************/
09616 {
09617    return _server_connection.send_sock;
09618 }
09619 
09620 
09621 /********************************************************************/
09622 int rpc_get_event_sock()
09623 /********************************************************************\
09624 
09625   Routine: rpc_get_event_sock
09626 
09627   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
09628            optimized event sending.
09629 
09630   Input:
09631     none
09632 
09633   Output:
09634     none
09635 
09636   Function value:
09637     int    socket
09638 
09639 \********************************************************************/
09640 {
09641    return _server_connection.event_sock;
09642 }
09643 
09644 /**dox***************************************************************/
09645 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09646 
09647 /********************************************************************/
09648 /**
09649 Send event residing in the TCP cache buffer filled by
09650            rpc_send_event. This routine should be called when a
09651            run is stopped.
09652 
09653 @return RPC_SUCCESS, RPC_NET_ERROR
09654 */
09655 INT rpc_flush_event()
09656 {
09657    INT i;
09658 
09659    if (!rpc_is_remote())
09660       return RPC_SUCCESS;
09661 
09662    /* return if rpc_send_event was not called */
09663    if (!_tcp_buffer || _tcp_wp == 0)
09664       return RPC_SUCCESS;
09665 
09666    /* empty TCP buffer */
09667    if (_tcp_wp > 0) {
09668       i = send_tcp(_server_connection.send_sock,
09669                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09670 
09671       if (i != _tcp_wp - _tcp_rp) {
09672          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
09673          return RPC_NET_ERROR;
09674       }
09675    }
09676 
09677    _tcp_rp = _tcp_wp = 0;
09678 
09679    return RPC_SUCCESS;
09680 }
09681 
09682 /**dox***************************************************************/
09683 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09684 
09685 /********************************************************************/
09686 
09687 typedef struct {
09688    int transition;
09689    int run_number;
09690    time_t trans_time;
09691    int sequence_number;
09692 } TR_FIFO;
09693 
09694 static TR_FIFO tr_fifo[10];
09695 static int trf_wp, trf_rp;
09696 
09697 static INT rpc_transition_dispatch(INT index, void *prpc_param[])
09698 /********************************************************************\
09699 
09700   Routine: rpc_transition_dispatch
09701 
09702   Purpose: Gets called when a transition function was registered and
09703            a transition occured. Internal use only.
09704 
09705   Input:
09706     INT    index            RPC function ID
09707     void   *prpc_param      RPC parameters
09708 
09709   Output:
09710     none
09711 
09712   Function value:
09713     INT    return value from called user routine
09714 
09715 \********************************************************************/
09716 {
09717    INT status, i;
09718 
09719    /* erase error string */
09720    *(CSTRING(2)) = 0;
09721 
09722    if (index == RPC_RC_TRANSITION) {
09723       for (i = 0; i < MAX_TRANSITIONS; i++)
09724          if (_trans_table[i].transition == CINT(0) &&
09725              _trans_table[i].sequence_number == CINT(4))
09726             break;
09727 
09728       /* call registerd function */
09729       if (i < MAX_TRANSITIONS) {
09730          if (_trans_table[i].func)
09731             /* execute callback if defined */
09732             status = _trans_table[i].func(CINT(1), CSTRING(2));
09733          else {
09734             /* store transition in FIFO */
09735             tr_fifo[trf_wp].transition = CINT(0);
09736             tr_fifo[trf_wp].run_number = CINT(1);
09737             tr_fifo[trf_wp].trans_time = time(NULL);
09738             tr_fifo[trf_wp].sequence_number = CINT(4);
09739             trf_wp = (trf_wp + 1) % 10;
09740             status = RPC_SUCCESS;
09741          }
09742       } else
09743          status = RPC_SUCCESS;
09744 
09745    } else {
09746       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
09747       status = RPC_INVALID_ID;
09748    }
09749 
09750    return status;
09751 }
09752 
09753 /********************************************************************/
09754 int cm_query_transition(int *transition, int *run_number, int *trans_time)
09755 /********************************************************************\
09756 
09757   Routine: cm_query_transition
09758 
09759   Purpose: Query system if transition has occured. Normally, one 
09760            registers callbacks for transitions via 
09761            cm_register_transition. In some environments however,
09762            callbacks are not possible. In that case one spciefies
09763            a NULL pointer as the callback routine and can query
09764            transitions "manually" by calling this functions. A small
09765            FIFO takes care that no transition is lost if this functions
09766            did not get called between some transitions.
09767 
09768   Output:
09769     INT   *transition        Type of transition, one of TR_xxx
09770     INT   *run_nuber         Run number for transition
09771     time_t *trans_time       Time (in UNIX time) of transition
09772 
09773   Function value:
09774     FALSE  No transition occured since last call
09775     TRUE   Transition occured
09776 
09777 \********************************************************************/
09778 {
09779 
09780    if (trf_wp == trf_rp)
09781       return FALSE;
09782 
09783    if (transition)
09784       *transition = tr_fifo[trf_rp].transition;
09785 
09786    if (run_number)
09787       *run_number = tr_fifo[trf_rp].run_number;
09788 
09789    if (trans_time)
09790       *trans_time = (int) tr_fifo[trf_rp].trans_time;
09791 
09792    trf_rp = (trf_rp + 1) % 10;
09793 
09794    return TRUE;
09795 }
09796 
09797 /********************************************************************\
09798 *                        server functions                            *
09799 \********************************************************************/
09800 
09801 
09802 /********************************************************************/
09803 INT recv_tcp_server(INT index, char *buffer, DWORD buffer_size, INT flags,
09804                     INT * remaining)
09805 /********************************************************************\
09806 
09807   Routine: recv_tcp_server
09808 
09809   Purpose: TCP receive routine with local cache. To speed up network
09810            performance, a 64k buffer is read in at once and split into
09811            several RPC command on successive calls to recv_tcp_server.
09812            Therefore, the number of recv() calls is minimized.
09813 
09814            This routine is ment to be called by the server process.
09815            Clients should call recv_tcp instead.
09816 
09817   Input:
09818     INT   index              Index of server connection
09819     DWORD buffer_size        Size of the buffer in bytes.
09820     INT   flags              Flags passed to recv()
09821     INT   convert_flags      Convert flags needed for big/little
09822                              endian conversion
09823 
09824   Output:
09825     char  *buffer            Network receive buffer.
09826     INT   *remaining         Remaining data in cache
09827 
09828   Function value:
09829     INT                      Same as recv()
09830 
09831 \********************************************************************/
09832 {
09833    INT size, param_size;
09834    NET_COMMAND *nc;
09835    INT write_ptr, read_ptr, misalign;
09836    char *net_buffer;
09837    INT copied, status;
09838    INT sock;
09839 
09840    sock = _server_acception[index].recv_sock;
09841 
09842    if (flags & MSG_PEEK) {
09843       status = recv(sock, buffer, buffer_size, flags);
09844       if (status == -1)
09845          cm_msg(MERROR, "recv_tcp_server",
09846                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
09847                 errno, strerror(errno));
09848       return status;
09849    }
09850 
09851    if (!_server_acception[index].net_buffer) {
09852       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
09853          _server_acception[index].net_buffer_size = NET_TCP_SIZE;
09854       else
09855          _server_acception[index].net_buffer_size = NET_BUFFER_SIZE;
09856 
09857       _server_acception[index].net_buffer =
09858           (char *) M_MALLOC(_server_acception[index].net_buffer_size);
09859       _server_acception[index].write_ptr = 0;
09860       _server_acception[index].read_ptr = 0;
09861       _server_acception[index].misalign = 0;
09862    }
09863    if (!_server_acception[index].net_buffer) {
09864       cm_msg(MERROR, "recv_tcp_server", "not enough memory to allocate network buffer");
09865       return -1;
09866    }
09867 
09868    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
09869       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
09870       return -1;
09871    }
09872 
09873    copied = 0;
09874    param_size = -1;
09875 
09876    write_ptr = _server_acception[index].write_ptr;
09877    read_ptr = _server_acception[index].read_ptr;
09878    misalign = _server_acception[index].misalign;
09879    net_buffer = _server_acception[index].net_buffer;
09880 
09881    do {
09882       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
09883          if (param_size == -1) {
09884             if (copied > 0) {
09885                /* assemble split header */
09886                memcpy(buffer + copied, net_buffer + read_ptr,
09887                       (INT) sizeof(NET_COMMAND_HEADER) - copied);
09888                nc = (NET_COMMAND *) (buffer);
09889             } else
09890                nc = (NET_COMMAND *) (net_buffer + read_ptr);
09891 
09892             param_size = (INT) nc->header.param_size;
09893 
09894             if (_server_acception[index].convert_flags)
09895                rpc_convert_single(&param_size, TID_DWORD, 0,
09896                                   _server_acception[index].convert_flags);
09897          }
09898 
09899          /* check if parameters fit in buffer */
09900          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
09901             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
09902             _server_acception[index].read_ptr = _server_acception[index].write_ptr = 0;
09903             return -1;
09904          }
09905 
09906          /* check if we have all parameters in buffer */
09907          if (write_ptr - read_ptr >=
09908              param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
09909             break;
09910       }
09911 
09912       /* not enough data, so copy partially and get new */
09913       size = write_ptr - read_ptr;
09914 
09915       if (size > 0) {
09916          memcpy(buffer + copied, net_buffer + read_ptr, size);
09917          copied += size;
09918          read_ptr = write_ptr;
09919       }
09920 #ifdef OS_UNIX
09921       do {
09922          write_ptr =
09923              recv(sock, net_buffer + misalign,
09924                   _server_acception[index].net_buffer_size - 8, flags);
09925 
09926          /* don't return if an alarm signal was cought */
09927       } while (write_ptr == -1 && errno == EINTR);
09928 #else
09929       write_ptr =
09930           recv(sock, net_buffer + misalign, _server_acception[index].net_buffer_size - 8,
09931                flags);
09932 #endif
09933 
09934       /* abort if connection broken */
09935       if (write_ptr <= 0) {
09936          cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)",
09937                 write_ptr, errno, strerror(errno));
09938 
09939          if (remaining)
09940             *remaining = 0;
09941 
09942          return write_ptr;
09943       }
09944 
09945       read_ptr = misalign;
09946       write_ptr += misalign;
09947 
09948       misalign = write_ptr % 8;
09949    } while (TRUE);
09950 
09951    /* copy rest of parameters */
09952    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
09953    memcpy(buffer + copied, net_buffer + read_ptr, size);
09954    read_ptr += size;
09955 
09956    if (remaining) {
09957       /* don't keep rpc_server_receive in an infinite loop */
09958       if (write_ptr - read_ptr < param_size)
09959          *remaining = 0;
09960       else
09961          *remaining = write_ptr - read_ptr;
09962    }
09963 
09964    _server_acception[index].write_ptr = write_ptr;
09965    _server_acception[index].read_ptr = read_ptr;
09966    _server_acception[index].misalign = misalign;
09967 
09968    return size + copied;
09969 }
09970 
09971 
09972 /********************************************************************/
09973 INT recv_tcp_check(int sock)
09974 /********************************************************************\
09975 
09976   Routine: recv_tcp_check
09977 
09978   Purpose: Check if in TCP receive buffer associated with sock is
09979            some data. Called by ss_suspend.
09980 
09981   Input:
09982     INT   sock               TCP receive socket
09983 
09984   Output:
09985     none
09986 
09987   Function value:
09988     INT   count              Number of bytes remaining in TCP buffer
09989 
09990 \********************************************************************/
09991 {
09992    INT index;
09993 
09994    /* figure out to which connection socket belongs */
09995    for (index = 0; index < MAX_RPC_CONNECTION; index++)
09996       if (_server_acception[index].recv_sock == sock)
09997          break;
09998 
09999    return _server_acception[index].write_ptr - _server_acception[index].read_ptr;
10000 }
10001 
10002 
10003 /********************************************************************/
10004 INT recv_event_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10005                       INT * remaining)
10006 /********************************************************************\
10007 
10008   Routine: recv_event_server
10009 
10010   Purpose: TCP event receive routine with local cache. To speed up
10011            network performance, a 64k buffer is read in at once and
10012            split into several RPC command on successive calls to
10013            recv_event_server. Therefore, the number of recv() calls
10014            is minimized.
10015 
10016            This routine is ment to be called by the server process.
10017            Clients should call recv_tcp instead.
10018 
10019   Input:
10020     INT   index              Index of server connection
10021     DWORD buffer_size        Size of the buffer in bytes.
10022     INT   flags              Flags passed to recv()
10023     INT   convert_flags      Convert flags needed for big/little
10024                              endian conversion
10025 
10026   Output:
10027     char  *buffer            Network receive buffer.
10028     INT   *remaining         Remaining data in cache
10029 
10030   Function value:
10031     INT                      Same as recv()
10032 
10033 \********************************************************************/
10034 {
10035    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10036    EVENT_HEADER *pevent;
10037    INT write_ptr, read_ptr, misalign;
10038    char *net_buffer;
10039    INT copied, status;
10040    INT sock;
10041    RPC_SERVER_ACCEPTION *psa;
10042 
10043    psa = &_server_acception[index];
10044    sock = psa->event_sock;
10045 
10046    if (flags & MSG_PEEK) {
10047       status = recv(sock, buffer, buffer_size, flags);
10048       if (status == -1)
10049          cm_msg(MERROR, "recv_event_server",
10050                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10051                 errno, strerror(errno));
10052       return status;
10053    }
10054 
10055    if (!psa->ev_net_buffer) {
10056       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10057          psa->net_buffer_size = NET_TCP_SIZE;
10058       else
10059          psa->net_buffer_size = NET_BUFFER_SIZE;
10060 
10061       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10062       psa->ev_write_ptr = 0;
10063       psa->ev_read_ptr = 0;
10064       psa->ev_misalign = 0;
10065    }
10066    if (!psa->ev_net_buffer) {
10067       cm_msg(MERROR, "recv_event_server", "not enough memory to allocate network buffer");
10068       return -1;
10069    }
10070 
10071    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10072 
10073    if ((INT) buffer_size < header_size) {
10074       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10075       return -1;
10076    }
10077 
10078    copied = 0;
10079    event_size = -1;
10080 
10081    write_ptr = psa->ev_write_ptr;
10082    read_ptr = psa->ev_read_ptr;
10083    misalign = psa->ev_misalign;
10084    net_buffer = psa->ev_net_buffer;
10085 
10086    do {
10087       if (write_ptr - read_ptr >= header_size - copied) {
10088          if (event_size == -1) {
10089             if (copied > 0) {
10090                /* assemble split header */
10091                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10092                pbh = (INT *) buffer;
10093             } else
10094                pbh = (INT *) (net_buffer + read_ptr);
10095 
10096             pevent = (EVENT_HEADER *) (pbh + 1);
10097 
10098             event_size = pevent->data_size;
10099             if (psa->convert_flags)
10100                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10101 
10102             aligned_event_size = ALIGN8(event_size);
10103          }
10104 
10105          /* check if data part fits in buffer */
10106          if ((INT) buffer_size < aligned_event_size + header_size) {
10107             cm_msg(MERROR, "recv_event_server",
10108                    "parameters too large for network buffer");
10109             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10110             return -1;
10111          }
10112 
10113          /* check if we have whole event in buffer */
10114          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10115             break;
10116       }
10117 
10118       /* not enough data, so copy partially and get new */
10119       size = write_ptr - read_ptr;
10120 
10121       if (size > 0) {
10122          memcpy(buffer + copied, net_buffer + read_ptr, size);
10123          copied += size;
10124          read_ptr = write_ptr;
10125       }
10126 #ifdef OS_UNIX
10127       do {
10128          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10129 
10130          /* don't return if an alarm signal was cought */
10131       } while (write_ptr == -1 && errno == EINTR);
10132 #else
10133       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10134 #endif
10135 
10136       /* abort if connection broken */
10137       if (write_ptr <= 0) {
10138          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)",
10139                 write_ptr, errno, strerror(errno));
10140 
10141          if (remaining)
10142             *remaining = 0;
10143 
10144          return write_ptr;
10145       }
10146 
10147       read_ptr = misalign;
10148       write_ptr += misalign;
10149 
10150       misalign = write_ptr % 8;
10151    } while (TRUE);
10152 
10153    /* copy rest of event */
10154    size = aligned_event_size + header_size - copied;
10155    if (size > 0) {
10156       memcpy(buffer + copied, net_buffer + read_ptr, size);
10157       read_ptr += size;
10158    }
10159 
10160    if (remaining)
10161       *remaining = write_ptr - read_ptr;
10162 
10163    psa->ev_write_ptr = write_ptr;
10164    psa->ev_read_ptr = read_ptr;
10165    psa->ev_misalign = misalign;
10166 
10167    /* convert header little endian/big endian */
10168    if (psa->convert_flags) {
10169       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10170 
10171       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10172       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10173       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10174       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10175       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10176       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10177    }
10178 
10179    return header_size + event_size;
10180 }
10181 
10182 
10183 /********************************************************************/
10184 INT recv_event_check(int sock)
10185 /********************************************************************\
10186 
10187   Routine: recv_event_check
10188 
10189   Purpose: Check if in TCP event receive buffer associated with sock
10190            is some data. Called by ss_suspend.
10191 
10192   Input:
10193     INT   sock               TCP receive socket
10194 
10195   Output:
10196     none
10197 
10198   Function value:
10199     INT   count              Number of bytes remaining in TCP buffer
10200 
10201 \********************************************************************/
10202 {
10203    INT index;
10204 
10205    /* figure out to which connection socket belongs */
10206    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10207       if (_server_acception[index].event_sock == sock)
10208          break;
10209 
10210    return _server_acception[index].ev_write_ptr - _server_acception[index].ev_read_ptr;
10211 }
10212 
10213 
10214 /********************************************************************/
10215 INT rpc_register_server(INT server_type, char *name, INT * port,
10216                         INT(*func) (INT, void **))
10217 /********************************************************************\
10218 
10219   Routine: rpc_register_server
10220 
10221   Purpose: Register the calling process as a MIDAS RPC server. Note
10222            that cm_connnect_experiment must be called prior to any call of
10223            rpc_register_server.
10224 
10225   Input:
10226     INT   server_type       One of the following constants:
10227                             ST_SINGLE: register a single process server
10228                             ST_MTHREAD: for each connection, start
10229                                         a new thread to serve it
10230                             ST_MPROCESS: for each connection, start
10231                                          a new process to server it
10232                             ST_SUBPROCESS: the routine was called from
10233                                            a multi process server
10234                             ST_REMOTE: register a client program server
10235                                        connected to the ODB
10236     char  *name             Name of .EXE file to start in MPROCESS mode
10237     INT   *port             TCP port for listen. NULL if listen as main
10238                             server (MIDAS_TCP_PORT is then used). If *port=0,
10239                             the OS chooses a free port and returns it. If
10240                             *port != 0, this port is used.
10241     INT   *func             Default dispatch function
10242 
10243   Output:
10244     INT   *port             Port under which server is listening.
10245 
10246   Function value:
10247     RPC_SUCCESS             Successful completion
10248     RPC_NET_ERROR           Error in socket call
10249     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10250 
10251 \********************************************************************/
10252 {
10253    struct sockaddr_in bind_addr;
10254    INT status, flag;
10255    int size;
10256 
10257 #ifdef OS_WINNT
10258    {
10259       WSADATA WSAData;
10260 
10261       /* Start windows sockets */
10262       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10263          return RPC_NET_ERROR;
10264    }
10265 #endif
10266 
10267    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10268 
10269    /* register system functions */
10270    rpc_register_functions(rpc_get_internal_list(0), func);
10271 
10272    if (name != NULL)
10273       rpc_set_server_option(RPC_OSERVER_NAME, (PTYPE) name);
10274 
10275    /* in subprocess mode, don't start listener */
10276    if (server_type == ST_SUBPROCESS)
10277       return RPC_SUCCESS;
10278 
10279    /* create a socket for listening */
10280    _lsock = socket(AF_INET, SOCK_STREAM, 0);
10281    if (_lsock == -1) {
10282       cm_msg(MERROR, "rpc_register_server", "socket() failed");
10283       return RPC_NET_ERROR;
10284    }
10285 
10286    /* reuse address, needed if previous server stopped (30s timeout!) */
10287    flag = 1;
10288    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
10289    if (status < 0) {
10290       cm_msg(MERROR, "rpc_register_server", "setsockopt() failed");
10291       return RPC_NET_ERROR;
10292    }
10293 
10294    /* bind local node name and port to socket */
10295    memset(&bind_addr, 0, sizeof(bind_addr));
10296    bind_addr.sin_family = AF_INET;
10297    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
10298 
10299    if (!port)
10300       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
10301    else
10302       bind_addr.sin_port = htons((short) (*port));
10303 
10304    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
10305    if (status < 0) {
10306       cm_msg(MERROR, "rpc_register_server", "bind() failed: %s", strerror(errno));
10307       return RPC_NET_ERROR;
10308    }
10309 
10310    /* listen for connection */
10311 #ifdef OS_MSDOS
10312    status = listen(_lsock, 1);
10313 #else
10314    status = listen(_lsock, SOMAXCONN);
10315 #endif
10316    if (status < 0) {
10317       cm_msg(MERROR, "rpc_register_server", "listen() failed");
10318       return RPC_NET_ERROR;
10319    }
10320 
10321    /* return port wich OS has choosen */
10322    if (port && *port == 0) {
10323       size = sizeof(bind_addr);
10324       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
10325       *port = ntohs(bind_addr.sin_port);
10326    }
10327 
10328    /* define callbacks for ss_suspend */
10329    if (server_type == ST_REMOTE)
10330       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
10331    else
10332       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
10333 
10334    return RPC_SUCCESS;
10335 }
10336 
10337 
10338 /********************************************************************/
10339 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
10340 /********************************************************************\
10341 
10342   Routine: rpc_execute
10343 
10344   Purpose: Execute a RPC command received over the network
10345 
10346   Input:
10347     INT  sock               TCP socket to which the result should be
10348                             send back
10349 
10350     char *buffer            Command buffer
10351     INT  convert_flags      Flags for data conversion
10352 
10353   Output:
10354     none
10355 
10356   Function value:
10357     RPC_SUCCESS             Successful completion
10358     RPC_INVALID_ID          Invalid routine_id received
10359     RPC_NET_ERROR           Error in socket call
10360     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10361     RPC_SHUTDOWN            Shutdown requested
10362     SS_ABORT                TCP connection broken
10363     SS_EXIT                 TCP connection closed
10364 
10365 \********************************************************************/
10366 {
10367    INT i, index, routine_id, status;
10368    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10369    INT tid, flags;
10370    NET_COMMAND *nc_in, *nc_out;
10371    INT param_size, max_size;
10372    void *prpc_param[20];
10373    char str[1024], debug_line[1024];
10374 
10375 /* return buffer must be auto for multi-thread servers */
10376    char return_buffer[NET_BUFFER_SIZE];
10377 
10378 
10379    /* extract pointer array to parameters */
10380    nc_in = (NET_COMMAND *) buffer;
10381    nc_out = (NET_COMMAND *) return_buffer;
10382 
10383    /* convert header format (byte swapping) */
10384    if (convert_flags) {
10385       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
10386       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
10387    }
10388 
10389    /* no result return in FAST TCP mode */
10390    if (nc_in->header.routine_id & TCP_FAST)
10391       sock = 0;
10392 
10393    /* find entry in rpc_list */
10394    routine_id = nc_in->header.routine_id & ~TCP_FAST;
10395 
10396    for (i = 0;; i++)
10397       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
10398          break;
10399    index = i;
10400    if (rpc_list[i].id == 0) {
10401       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
10402       return RPC_INVALID_ID;
10403    }
10404 
10405    in_param_ptr = nc_in->param;
10406    out_param_ptr = nc_out->param;
10407 
10408    sprintf(debug_line, "%s(", rpc_list[index].name);
10409 
10410    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10411       tid = rpc_list[index].param[i].tid;
10412       flags = rpc_list[index].param[i].flags;
10413 
10414       if (flags & RPC_IN) {
10415          param_size = ALIGN8(tid_size[tid]);
10416 
10417          if (tid == TID_STRING || tid == TID_LINK)
10418             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10419 
10420          if (flags & RPC_VARARRAY) {
10421             /* for arrays, the size is stored as a INT in front of the array */
10422             param_size = *((INT *) in_param_ptr);
10423             if (convert_flags)
10424                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
10425             param_size = ALIGN8(param_size);
10426 
10427             in_param_ptr += ALIGN8(sizeof(INT));
10428          }
10429 
10430          if (tid == TID_STRUCT)
10431             param_size = ALIGN8(rpc_list[index].param[i].n);
10432 
10433          prpc_param[i] = in_param_ptr;
10434 
10435          /* convert data format */
10436          if (convert_flags) {
10437             if (flags & RPC_VARARRAY)
10438                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
10439             else
10440                rpc_convert_data(in_param_ptr, tid, flags,
10441                                 rpc_list[index].param[i].n * tid_size[tid],
10442                                 convert_flags);
10443          }
10444 
10445          db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
10446          if (rpc_list[index].param[i].tid == TID_STRING) {
10447             /* check for long strings (db_create_record...) */
10448             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10449                strcat(debug_line, "\"");
10450                strcat(debug_line, str);
10451                strcat(debug_line, "\"");
10452             } else
10453                strcat(debug_line, "...");
10454          } else
10455             strcat(debug_line, str);
10456 
10457          in_param_ptr += param_size;
10458       }
10459 
10460       if (flags & RPC_OUT) {
10461          param_size = ALIGN8(tid_size[tid]);
10462 
10463          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10464             /* save maximum array length */
10465             max_size = *((INT *) in_param_ptr);
10466             if (convert_flags)
10467                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
10468             max_size = ALIGN8(max_size);
10469 
10470             *((INT *) out_param_ptr) = max_size;
10471 
10472             /* save space for return array length */
10473             out_param_ptr += ALIGN8(sizeof(INT));
10474 
10475             /* use maximum array length from input */
10476             param_size += max_size;
10477          }
10478 
10479          if (rpc_list[index].param[i].tid == TID_STRUCT)
10480             param_size = ALIGN8(rpc_list[index].param[i].n);
10481 
10482          if ((PTYPE) out_param_ptr - (PTYPE) nc_out + param_size > NET_BUFFER_SIZE) {
10483             cm_msg(MERROR, "rpc_execute",
10484                    "return parameters (%d) too large for network buffer (%d)",
10485                    (PTYPE) out_param_ptr - (PTYPE) nc_out + param_size, NET_BUFFER_SIZE);
10486             return RPC_EXCEED_BUFFER;
10487          }
10488 
10489          /* if parameter goes both directions, copy input to output */
10490          if (rpc_list[index].param[i].flags & RPC_IN)
10491             memcpy(out_param_ptr, prpc_param[i], param_size);
10492 
10493          if (_debug_print && !(flags & RPC_IN))
10494             strcat(debug_line, "-");
10495 
10496          prpc_param[i] = out_param_ptr;
10497          out_param_ptr += param_size;
10498       }
10499 
10500       if (rpc_list[index].param[i + 1].tid)
10501          strcat(debug_line, ", ");
10502    }
10503 
10504    strcat(debug_line, ")");
10505    rpc_debug_printf(debug_line);
10506 
10507    last_param_ptr = out_param_ptr;
10508 
10509   /*********************************\
10510   *   call dispatch function        *
10511   \*********************************/
10512    if (rpc_list[index].dispatch)
10513       status = rpc_list[index].dispatch(routine_id, prpc_param);
10514    else
10515       status = RPC_INVALID_ID;
10516 
10517    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
10518        routine_id == RPC_ID_WATCHDOG)
10519       status = RPC_SUCCESS;
10520 
10521    /* return immediately for closed down client connections */
10522    if (!sock && routine_id == RPC_ID_EXIT)
10523       return SS_EXIT;
10524 
10525    if (!sock && routine_id == RPC_ID_SHUTDOWN)
10526       return RPC_SHUTDOWN;
10527 
10528    /* Return if TCP connection broken */
10529    if (status == SS_ABORT)
10530       return SS_ABORT;
10531 
10532    /* if sock == 0, we are in FTCP mode and may not sent results */
10533    if (!sock)
10534       return RPC_SUCCESS;
10535 
10536    /* compress variable length arrays */
10537    out_param_ptr = nc_out->param;
10538    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10539       if (rpc_list[index].param[i].flags & RPC_OUT) {
10540          tid = rpc_list[index].param[i].tid;
10541          flags = rpc_list[index].param[i].flags;
10542          param_size = ALIGN8(tid_size[tid]);
10543 
10544          if (tid == TID_STRING) {
10545             max_size = *((INT *) out_param_ptr);
10546             param_size = strlen((char *) prpc_param[i]) + 1;
10547             param_size = ALIGN8(param_size);
10548 
10549             /* move string ALIGN8(sizeof(INT)) left */
10550             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
10551 
10552             /* move remaining parameters to end of string */
10553             memcpy(out_param_ptr + param_size,
10554                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
10555                    (PTYPE) last_param_ptr -
10556                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
10557          }
10558 
10559          if (flags & RPC_VARARRAY) {
10560             /* store array length at current out_param_ptr */
10561             max_size = *((INT *) out_param_ptr);
10562             param_size = *((INT *) prpc_param[i + 1]);
10563             *((INT *) out_param_ptr) = param_size;
10564             if (convert_flags)
10565                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
10566 
10567             out_param_ptr += ALIGN8(sizeof(INT));
10568 
10569             param_size = ALIGN8(param_size);
10570 
10571             /* move remaining parameters to end of array */
10572             memcpy(out_param_ptr + param_size,
10573                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
10574                    (PTYPE) last_param_ptr -
10575                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
10576          }
10577 
10578          if (tid == TID_STRUCT)
10579             param_size = ALIGN8(rpc_list[index].param[i].n);
10580 
10581          /* convert data format */
10582          if (convert_flags) {
10583             if (flags & RPC_VARARRAY)
10584                rpc_convert_data(out_param_ptr, tid,
10585                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
10586                                 param_size, convert_flags);
10587             else
10588                rpc_convert_data(out_param_ptr, tid,
10589                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
10590                                 rpc_list[index].param[i].n * tid_size[tid],
10591                                 convert_flags);
10592          }
10593 
10594          out_param_ptr += param_size;
10595       }
10596 
10597    /* send return parameters */
10598    param_size = (PTYPE) out_param_ptr - (PTYPE) nc_out->param;
10599    nc_out->header.routine_id = status;
10600    nc_out->header.param_size = param_size;
10601 
10602    /* convert header format (byte swapping) if necessary */
10603    if (convert_flags) {
10604       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD,
10605                          RPC_OUTGOING, convert_flags);
10606       rpc_convert_single(&nc_out->header.param_size, TID_DWORD,
10607                          RPC_OUTGOING, convert_flags);
10608    }
10609 
10610    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
10611 
10612    if (status < 0) {
10613       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
10614       return RPC_NET_ERROR;
10615    }
10616 
10617    /* print return buffer */
10618 /*
10619   printf("Return buffer, ID %d:\n", routine_id);
10620   for (i=0; i<param_size ; i++)
10621     {
10622     status = (char) nc_out->param[i];
10623     printf("%02X ", status);
10624     if (i%8 == 7)
10625       printf("\n");
10626     }
10627 */
10628    /* return SS_EXIT if RPC_EXIT is called */
10629    if (routine_id == RPC_ID_EXIT)
10630       return SS_EXIT;
10631 
10632    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
10633    if (routine_id == RPC_ID_SHUTDOWN)
10634       return RPC_SHUTDOWN;
10635 
10636    return RPC_SUCCESS;
10637 }
10638 
10639 
10640 /********************************************************************/
10641 INT rpc_execute_ascii(INT sock, char *buffer)
10642 /********************************************************************\
10643 
10644   Routine: rpc_execute_ascii
10645 
10646   Purpose: Execute a RPC command received over the network in ASCII
10647            mode
10648 
10649   Input:
10650     INT  sock               TCP socket to which the result should be
10651                             send back
10652 
10653     char *buffer            Command buffer
10654 
10655   Output:
10656     none
10657 
10658   Function value:
10659     RPC_SUCCESS             Successful completion
10660     RPC_INVALID_ID          Invalid routine_id received
10661     RPC_NET_ERROR           Error in socket call
10662     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10663     RPC_SHUTDOWN            Shutdown requested
10664     SS_ABORT                TCP connection broken
10665     SS_EXIT                 TCP connection closed
10666 
10667 \********************************************************************/
10668 {
10669 #define ASCII_BUFFER_SIZE 64500
10670 #define N_APARAM           1024
10671 
10672    INT i, j, index, status, index_in;
10673    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10674    INT routine_id, tid, flags, array_tid, n_param;
10675    INT param_size, item_size, num_values;
10676    void *prpc_param[20];
10677    char *arpc_param[N_APARAM], *pc;
10678    char str[1024], debug_line[1024];
10679    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
10680    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
10681    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
10682 
10683    /* parse arguments */
10684    arpc_param[0] = buffer;
10685    for (i = 1; i < N_APARAM; i++) {
10686       arpc_param[i] = strchr(arpc_param[i - 1], '&');
10687       if (arpc_param[i] == NULL)
10688          break;
10689       *arpc_param[i] = 0;
10690       arpc_param[i]++;
10691    }
10692 
10693    /* decode '%' */
10694    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
10695       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
10696          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
10697             str[0] = pc[1];
10698             str[1] = pc[2];
10699             str[2] = 0;
10700             sscanf(str, "%02X", &i);
10701 
10702             *pc++ = i;
10703             while (pc[2]) {
10704                pc[0] = pc[2];
10705                pc++;
10706             }
10707          }
10708       }
10709 
10710    /* find entry in rpc_list */
10711    for (i = 0;; i++)
10712       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
10713          break;
10714    index = i;
10715    routine_id = rpc_list[i].id;
10716    if (rpc_list[i].id == 0) {
10717       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
10718       return RPC_INVALID_ID;
10719    }
10720 
10721    in_param_ptr = buffer1;
10722    out_param_ptr = buffer2;
10723    index_in = 1;
10724 
10725    sprintf(debug_line, "%s(", rpc_list[index].name);
10726 
10727    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10728       tid = rpc_list[index].param[i].tid;
10729       flags = rpc_list[index].param[i].flags;
10730 
10731       if (flags & RPC_IN) {
10732          if (flags & RPC_VARARRAY) {
10733             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
10734 
10735             prpc_param[i] = in_param_ptr;
10736             for (j = 0; j < n_param; j++) {
10737                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
10738                in_param_ptr += param_size;
10739             }
10740             in_param_ptr = (char *) ALIGN8(((PTYPE) in_param_ptr));
10741 
10742             strcat(debug_line, "<array>");
10743          } else {
10744             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
10745             param_size = ALIGN8(param_size);
10746 
10747             if (tid == TID_STRING || tid == TID_LINK)
10748                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10749 
10750             /*
10751                if (tid == TID_STRUCT)
10752                param_size = ALIGN8( rpc_list[index].param[i].n );
10753              */
10754             prpc_param[i] = in_param_ptr;
10755 
10756             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
10757             if (rpc_list[index].param[i].tid == TID_STRING) {
10758                /* check for long strings (db_create_record...) */
10759                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10760                   strcat(debug_line, "\"");
10761                   strcat(debug_line, str);
10762                   strcat(debug_line, "\"");
10763                } else
10764                   strcat(debug_line, "...");
10765             } else
10766                strcat(debug_line, str);
10767 
10768             in_param_ptr += param_size;
10769          }
10770 
10771          if ((PTYPE) in_param_ptr - (PTYPE) buffer1 > ASCII_BUFFER_SIZE) {
10772             cm_msg(MERROR, "rpc_ascii_execute",
10773                    "parameters (%d) too large for network buffer (%d)", param_size,
10774                    ASCII_BUFFER_SIZE);
10775             return RPC_EXCEED_BUFFER;
10776          }
10777 
10778       }
10779 
10780       if (flags & RPC_OUT) {
10781          param_size = ALIGN8(tid_size[tid]);
10782 
10783          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10784             /* reserve maximum array length */
10785             param_size = atoi(arpc_param[index_in]);
10786             param_size = ALIGN8(param_size);
10787          }
10788 
10789 /*
10790       if (rpc_list[index].param[i].tid == TID_STRUCT)
10791         param_size = ALIGN8( rpc_list[index].param[i].n );
10792 */
10793          if ((PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size > ASCII_BUFFER_SIZE) {
10794             cm_msg(MERROR, "rpc_execute",
10795                    "return parameters (%d) too large for network buffer (%d)",
10796                    (PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size,
10797                    ASCII_BUFFER_SIZE);
10798             return RPC_EXCEED_BUFFER;
10799          }
10800 
10801          /* if parameter goes both directions, copy input to output */
10802          if (rpc_list[index].param[i].flags & RPC_IN)
10803             memcpy(out_param_ptr, prpc_param[i], param_size);
10804 
10805          if (!(flags & RPC_IN))
10806             strcat(debug_line, "-");
10807 
10808          prpc_param[i] = out_param_ptr;
10809          out_param_ptr += param_size;
10810       }
10811 
10812       if (rpc_list[index].param[i + 1].tid)
10813          strcat(debug_line, ", ");
10814    }
10815 
10816    strcat(debug_line, ")");
10817    rpc_debug_printf(debug_line);
10818 
10819    last_param_ptr = out_param_ptr;
10820 
10821    /*********************************\
10822    *   call dispatch function        *
10823    \*********************************/
10824 
10825    if (rpc_list[index].dispatch)
10826       status = rpc_list[index].dispatch(routine_id, prpc_param);
10827    else
10828       status = RPC_INVALID_ID;
10829 
10830    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
10831        routine_id == RPC_ID_WATCHDOG)
10832       status = RPC_SUCCESS;
10833 
10834    /* Return if TCP connection broken */
10835    if (status == SS_ABORT)
10836       return SS_ABORT;
10837 
10838    /* if sock == 0, we are in FTCP mode and may not sent results */
10839    if (!sock)
10840       return RPC_SUCCESS;
10841 
10842    /* send return status */
10843    out_param_ptr = return_buffer;
10844    sprintf(out_param_ptr, "%d", status);
10845    out_param_ptr += strlen(out_param_ptr);
10846 
10847    /* convert return parameters */
10848    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10849       if (rpc_list[index].param[i].flags & RPC_OUT) {
10850          *out_param_ptr++ = '&';
10851 
10852          tid = rpc_list[index].param[i].tid;
10853          flags = rpc_list[index].param[i].flags;
10854          param_size = ALIGN8(tid_size[tid]);
10855 
10856          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
10857             strcpy(out_param_ptr, (char *) prpc_param[i]);
10858             param_size = strlen((char *) prpc_param[i]);
10859          }
10860 
10861          else if (flags & RPC_VARARRAY) {
10862             if (rpc_list[index].id == RPC_BM_RECEIVE_EVENT) {
10863                param_size = *((INT *) prpc_param[i + 1]);
10864                /* write number of bytes to output */
10865                sprintf(out_param_ptr, "%d", param_size);
10866                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
10867                memcpy(out_param_ptr, prpc_param[i], param_size);
10868                out_param_ptr += param_size;
10869                *out_param_ptr = 0;
10870             } else {
10871                if (rpc_list[index].id == RPC_DB_GET_DATA1) {
10872                   param_size = *((INT *) prpc_param[i + 1]);
10873                   array_tid = *((INT *) prpc_param[i + 2]);
10874                   num_values = *((INT *) prpc_param[i + 3]);
10875                } else if (rpc_list[index].id == RPC_DB_GET_DATA_INDEX) {
10876                   param_size = *((INT *) prpc_param[i + 1]);
10877                   array_tid = *((INT *) prpc_param[i + 3]);
10878                   num_values = 1;
10879                } else if (rpc_list[index].id == RPC_HS_READ) {
10880                   param_size = *((INT *) prpc_param[i + 1]);
10881                   if (i == 6) {
10882                      array_tid = TID_DWORD;
10883                      num_values = param_size / sizeof(DWORD);
10884                   } else {
10885                      array_tid = *((INT *) prpc_param[10]);
10886                      num_values = *((INT *) prpc_param[11]);
10887                   }
10888                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
10889 
10890                   param_size = *((INT *) prpc_param[i + 1]);
10891                   array_tid = tid;
10892                   if (tid == TID_STRING)
10893                      num_values = param_size / NAME_LENGTH;
10894                   else
10895                      num_values = param_size / tid_size[tid];
10896                }
10897 
10898                /* derive size of individual item */
10899                if (array_tid == TID_STRING)
10900                   item_size = param_size / num_values;
10901                else
10902                   item_size = tid_size[array_tid];
10903 
10904                /* write number of elements to output */
10905                sprintf(out_param_ptr, "%d", num_values);
10906                out_param_ptr += strlen(out_param_ptr);
10907 
10908                /* write array of values to output */
10909                for (j = 0; j < num_values; j++) {
10910                   *out_param_ptr++ = '&';
10911                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
10912                   out_param_ptr += strlen(out_param_ptr);
10913                }
10914             }
10915          }
10916 
10917 /*
10918       else if (tid == TID_STRUCT)
10919         param_size = ALIGN8( rpc_list[index].param[i].n );
10920 */
10921          else
10922             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
10923 
10924          out_param_ptr += strlen(out_param_ptr);
10925 
10926          if ((PTYPE) out_param_ptr - (PTYPE) return_buffer > ASCII_BUFFER_SIZE) {
10927             cm_msg(MERROR, "rpc_execute",
10928                    "return parameter (%d) too large for network buffer (%d)", param_size,
10929                    ASCII_BUFFER_SIZE);
10930             return RPC_EXCEED_BUFFER;
10931          }
10932       }
10933 
10934    /* send return parameters */
10935    param_size = (PTYPE) out_param_ptr - (PTYPE) return_buffer + 1;
10936 
10937    status = send_tcp(sock, return_buffer, param_size, 0);
10938 
10939    if (status < 0) {
10940       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
10941       return RPC_NET_ERROR;
10942    }
10943 
10944    /* print return buffer */
10945    if (strlen(return_buffer) > sizeof(debug_line)) {
10946       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
10947       strcat(debug_line, "...");
10948    } else
10949       sprintf(debug_line, "-> %s", return_buffer);
10950    rpc_debug_printf(debug_line);
10951 
10952    /* return SS_EXIT if RPC_EXIT is called */
10953    if (routine_id == RPC_ID_EXIT)
10954       return SS_EXIT;
10955 
10956    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
10957    if (routine_id == RPC_ID_SHUTDOWN)
10958       return RPC_SHUTDOWN;
10959 
10960    return RPC_SUCCESS;
10961 }
10962 
10963 
10964 /********************************************************************/
10965 INT rpc_server_accept(int lsock)
10966 /********************************************************************\
10967 
10968   Routine: rpc_server_accept
10969 
10970   Purpose: Accept new incoming connections
10971 
10972   Input:
10973     INT    lscok            Listen socket
10974 
10975   Output:
10976     none
10977 
10978   Function value:
10979     RPC_SUCCESS             Successful completion
10980     RPC_NET_ERROR           Error in socket call
10981     RPC_CONNCLOSED          Connection was closed
10982     RPC_SHUTDOWN            Listener shutdown
10983     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10984 
10985 \********************************************************************/
10986 {
10987    INT index, i;
10988    int size, status;
10989    char command, version[32], v1[32];
10990    INT sock, port1, port2, port3;
10991    struct sockaddr_in acc_addr;
10992    struct hostent *phe;
10993    char str[100];
10994    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
10995    char debug_str[30];
10996    char *argv[10];
10997    char net_buffer[256];
10998    struct linger ling;
10999 
11000    static struct callback_addr callback;
11001 
11002    if (lsock > 0) {
11003       size = sizeof(acc_addr);
11004       sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11005 
11006       if (sock == -1)
11007          return RPC_NET_ERROR;
11008    } else {
11009       /* lsock is stdin -> already connected from inetd */
11010 
11011       size = sizeof(acc_addr);
11012       sock = lsock;
11013       getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11014    }
11015 
11016    /* receive string with timeout */
11017    i = recv_string(sock, net_buffer, 256, 10000);
11018    rpc_debug_printf("Received command: %s", net_buffer);
11019 
11020    if (i > 0) {
11021       command = (char) toupper(net_buffer[0]);
11022 
11023       switch (command) {
11024       case 'S':
11025 
11026          /*----------- shutdown listener ----------------------*/
11027          closesocket(sock);
11028          return RPC_SHUTDOWN;
11029 
11030       case 7:
11031          ss_shell(sock);
11032          closesocket(sock);
11033          break;
11034 
11035       case 'I':
11036 
11037          /*----------- return available experiments -----------*/
11038          cm_scan_experiments();
11039          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11040             sprintf(str, "%s", exptab[i].name);
11041             send(sock, str, strlen(str) + 1, 0);
11042          }
11043          send(sock, "", 1, 0);
11044          closesocket(sock);
11045          break;
11046 
11047       case 'C':
11048 
11049          /*----------- connect to experiment -----------*/
11050 
11051          /* get callback information */
11052          callback.experiment[0] = 0;
11053          port1 = port2 = version[0] = 0;
11054          sscanf(net_buffer + 2, "%d %d %d %s", &port1, &port2, &port3, version);
11055          strcpy(callback.experiment,
11056                 strchr(strchr(strchr(strchr(net_buffer + 2, ' ') + 1, ' ') + 1, ' ') + 1,
11057                        ' ') + 1);
11058 
11059          /* print warning if version patch level doesn't agree */
11060          strcpy(v1, version);
11061          if (strchr(v1, '.'))
11062             if (strchr(strchr(v1, '.') + 1, '.'))
11063                *strchr(strchr(v1, '.') + 1, '.') = 0;
11064 
11065          strcpy(str, cm_get_version());
11066          if (strchr(str, '.'))
11067             if (strchr(strchr(str, '.') + 1, '.'))
11068                *strchr(strchr(str, '.') + 1, '.') = 0;
11069 
11070          if (strcmp(v1, str) != 0) {
11071             sprintf(str, "client MIDAS version %s differs from local version %s",
11072                     version, cm_get_version());
11073             cm_msg(MERROR, "rpc_server_accept", str);
11074 
11075             sprintf(str, "received string: %s", net_buffer + 2);
11076             cm_msg(MERROR, "rpc_server_accept", str);
11077          }
11078 
11079          callback.host_port1 = (short) port1;
11080          callback.host_port2 = (short) port2;
11081          callback.host_port3 = (short) port3;
11082          callback.debug = _debug_mode;
11083 
11084          /* get the name of the remote host */
11085 #ifdef OS_VXWORKS
11086          {
11087             INT status;
11088             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11089             if (status != 0) {
11090                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11091                break;
11092             }
11093          }
11094 #else
11095          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11096          if (phe == NULL) {
11097             /* use IP number instead */
11098             strcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr));
11099          } else
11100             strcpy(callback.host_name, phe->h_name);
11101 #endif
11102 
11103          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11104             /* update experiment definition */
11105             cm_scan_experiments();
11106 
11107             /* lookup experiment */
11108             if (equal_ustring(callback.experiment, "Default"))
11109                index = 0;
11110             else
11111                for (index = 0; index < MAX_EXPERIMENT && exptab[index].name[0]; index++)
11112                   if (equal_ustring(callback.experiment, exptab[index].name))
11113                      break;
11114 
11115             if (index == MAX_EXPERIMENT || exptab[index].name[0] == 0) {
11116                sprintf(str, "experiment %s not defined in exptab\r", callback.experiment);
11117                cm_msg(MERROR, "rpc_server_accept", str);
11118 
11119                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11120                closesocket(sock);
11121                break;
11122             }
11123 
11124             strcpy(callback.directory, exptab[index].directory);
11125             strcpy(callback.user, exptab[index].user);
11126 
11127             /* create a new process */
11128             sprintf(host_port1_str, "%d", callback.host_port1);
11129             sprintf(host_port2_str, "%d", callback.host_port2);
11130             sprintf(host_port3_str, "%d", callback.host_port3);
11131             sprintf(debug_str, "%d", callback.debug);
11132 
11133             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11134             argv[1] = callback.host_name;
11135             argv[2] = host_port1_str;
11136             argv[3] = host_port2_str;
11137             argv[4] = host_port3_str;
11138             argv[5] = debug_str;
11139             argv[6] = callback.experiment;
11140             argv[7] = callback.directory;
11141             argv[8] = callback.user;
11142             argv[9] = NULL;
11143 
11144             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
11145                              argv[0], argv[1], argv[2], argv[3], argv[4],
11146                              argv[5], argv[6], argv[7], argv[8], argv[9]);
11147 
11148             status = ss_spawnv(P_NOWAIT,
11149                                (char *) rpc_get_server_option(RPC_OSERVER_NAME), argv);
11150 
11151             if (status != SS_SUCCESS) {
11152                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
11153 
11154                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
11155                send(sock, str, strlen(str) + 1, 0);
11156                closesocket(sock);
11157                break;
11158             }
11159 
11160             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11161             send(sock, str, strlen(str) + 1, 0);
11162             closesocket(sock);
11163          } else {
11164             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11165             send(sock, str, strlen(str) + 1, 0);
11166             closesocket(sock);
11167          }
11168 
11169          /* look for next free entry */
11170          for (index = 0; index < MAX_RPC_CONNECTION; index++)
11171             if (_server_acception[index].recv_sock == 0)
11172                break;
11173          if (index == MAX_RPC_CONNECTION)
11174             return RPC_NET_ERROR;
11175          callback.index = index;
11176 
11177         /*----- multi thread server ------------------------*/
11178          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11179             ss_thread_create(rpc_server_thread, (void *) (&callback));
11180 
11181         /*----- single thread server -----------------------*/
11182          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
11183              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11184             rpc_server_callback(&callback);
11185 
11186          break;
11187 
11188       default:
11189          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c'", command);
11190          closesocket(sock);
11191          break;
11192 
11193       }
11194    } else {                     /* if i>0 */
11195 
11196       /* lingering needed for PCTCP */
11197       ling.l_onoff = 1;
11198       ling.l_linger = 0;
11199       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11200       closesocket(sock);
11201    }
11202 
11203    return RPC_SUCCESS;
11204 }
11205 
11206 
11207 /********************************************************************/
11208 INT rpc_client_accept(int lsock)
11209 /********************************************************************\
11210 
11211   Routine: rpc_client_accept
11212 
11213   Purpose: Accept new incoming connections as a client
11214 
11215   Input:
11216     INT    lsock            Listen socket
11217 
11218   Output:
11219     none
11220 
11221   Function value:
11222     RPC_SUCCESS             Successful completion
11223     RPC_NET_ERROR           Error in socket call
11224     RPC_CONNCLOSED          Connection was closed
11225     RPC_SHUTDOWN            Listener shutdown
11226     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11227 
11228 \********************************************************************/
11229 {
11230    INT index, i, version, status;
11231    int size, sock;
11232    struct sockaddr_in acc_addr;
11233    INT client_hw_type = 0, hw_type;
11234    char str[100], client_program[NAME_LENGTH];
11235    char host_name[HOST_NAME_LENGTH];
11236    INT convert_flags;
11237    char net_buffer[256], *p;
11238 
11239    size = sizeof(acc_addr);
11240    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11241 
11242    if (sock == -1)
11243       return RPC_NET_ERROR;
11244 
11245    /* get the name of the calling host */
11246 /* outcommented for speed reasons SR 7.10.98
11247 #ifdef OS_VXWORKS
11248   {
11249   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
11250   if (status != 0)
11251     strcpy(host_name, "unknown");
11252   }
11253 #else
11254   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11255   if (phe == NULL)
11256     strcpy(host_name, "unknown");
11257   strcpy(host_name, phe->h_name);
11258 #endif
11259 */
11260    strcpy(host_name, "");
11261 
11262    /* look for next free entry */
11263    for (index = 0; index < MAX_RPC_CONNECTION; index++)
11264       if (_server_acception[index].recv_sock == 0)
11265          break;
11266    if (index == MAX_RPC_CONNECTION) {
11267       closesocket(sock);
11268       return RPC_NET_ERROR;
11269    }
11270 
11271    /* receive string with timeout */
11272    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
11273    if (i <= 0) {
11274       closesocket(sock);
11275       return RPC_NET_ERROR;
11276    }
11277 
11278    /* get remote computer info */
11279    p = strtok(net_buffer, " ");
11280    if (p != NULL) {
11281       client_hw_type = atoi(p);
11282       p = strtok(NULL, " ");
11283    }
11284    if (p != NULL) {
11285       version = atoi(p);
11286       p = strtok(NULL, " ");
11287    }
11288    if (p != NULL) {
11289       strcpy(client_program, p);
11290       p = strtok(NULL, " ");
11291    }
11292    if (p != NULL) {
11293       strcpy(host_name, p);
11294       p = strtok(NULL, " ");
11295    }
11296 
11297    /* save information in _server_acception structure */
11298    _server_acception[index].recv_sock = sock;
11299    _server_acception[index].send_sock = 0;
11300    _server_acception[index].event_sock = 0;
11301    _server_acception[index].remote_hw_type = client_hw_type;
11302    strcpy(_server_acception[index].host_name, host_name);
11303    strcpy(_server_acception[index].prog_name, client_program);
11304    _server_acception[index].tid = ss_gettid();
11305    _server_acception[index].last_activity = ss_millitime();
11306    _server_acception[index].watchdog_timeout = 0;
11307 
11308    /* send my own computer id */
11309    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11310    sprintf(str, "%d %s", hw_type, cm_get_version());
11311    status = send(sock, str, strlen(str) + 1, 0);
11312    if (status != (INT) strlen(str) + 1)
11313       return RPC_NET_ERROR;
11314 
11315    rpc_set_server_acception(index + 1);
11316    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11317    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11318 
11319    /* set callback function for ss_suspend */
11320    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11321                            (int (*)(void)) rpc_server_receive);
11322 
11323    return RPC_SUCCESS;
11324 }
11325 
11326 
11327 /********************************************************************/
11328 INT rpc_server_callback(struct callback_addr * pcallback)
11329 /********************************************************************\
11330 
11331   Routine: rpc_server_callback
11332 
11333   Purpose: Callback a remote client. Setup _server_acception entry
11334            with optional conversion flags and establish two-way
11335            TCP connection.
11336 
11337   Input:
11338     callback_addr pcallback Pointer to a callback structure
11339 
11340   Output:
11341     none
11342 
11343   Function value:
11344     RPC_SUCCESS             Successful completion
11345 
11346 \********************************************************************/
11347 {
11348    INT index, status;
11349    int recv_sock, send_sock, event_sock;
11350    struct sockaddr_in bind_addr;
11351    struct hostent *phe;
11352    char str[100], client_program[NAME_LENGTH];
11353    char host_name[HOST_NAME_LENGTH];
11354    INT client_hw_type, hw_type;
11355    INT convert_flags;
11356    char net_buffer[256];
11357    struct callback_addr callback;
11358    int flag;
11359 
11360    /* copy callback information */
11361    memcpy(&callback, pcallback, sizeof(callback));
11362    index = callback.index;
11363 
11364    /* create new sockets for TCP */
11365    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
11366    send_sock = socket(AF_INET, SOCK_STREAM, 0);
11367    event_sock = socket(AF_INET, SOCK_STREAM, 0);
11368    if (event_sock == -1)
11369       return RPC_NET_ERROR;
11370 
11371    /* callback to remote node */
11372    memset(&bind_addr, 0, sizeof(bind_addr));
11373    bind_addr.sin_family = AF_INET;
11374    bind_addr.sin_port = htons(callback.host_port1);
11375 
11376 #ifdef OS_VXWORKS
11377    {
11378       INT host_addr;
11379 
11380       host_addr = hostGetByName(callback.host_name);
11381       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
11382    }
11383 #else
11384    phe = gethostbyname(callback.host_name);
11385    if (phe == NULL) {
11386       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
11387       return RPC_NET_ERROR;
11388    }
11389    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
11390 #endif
11391 
11392    /* connect receive socket */
11393 #ifdef OS_UNIX
11394    do {
11395       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
11396 
11397       /* don't return if an alarm signal was cought */
11398    } while (status == -1 && errno == EINTR);
11399 #else
11400    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11401 #endif
11402 
11403    if (status != 0) {
11404       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
11405       goto error;
11406    }
11407 
11408    bind_addr.sin_port = htons(callback.host_port2);
11409 
11410    /* connect send socket */
11411 #ifdef OS_UNIX
11412    do {
11413       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11414 
11415       /* don't return if an alarm signal was cought */
11416    } while (status == -1 && errno == EINTR);
11417 #else
11418    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11419 #endif
11420 
11421    if (status != 0) {
11422       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
11423       goto error;
11424    }
11425 
11426    bind_addr.sin_port = htons(callback.host_port3);
11427 
11428    /* connect event socket */
11429 #ifdef OS_UNIX
11430    do {
11431       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11432 
11433       /* don't return if an alarm signal was cought */
11434    } while (status == -1 && errno == EINTR);
11435 #else
11436    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11437 #endif
11438 
11439    if (status != 0) {
11440       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
11441       goto error;
11442    }
11443 
11444    /* increase receive buffer size to 64k */
11445 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
11446    flag = 0x10000;
11447    setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
11448 #endif
11449 
11450    if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
11451       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
11452       goto error;
11453    }
11454 
11455    /* get remote computer info */
11456    sscanf(net_buffer, "%d", &client_hw_type);
11457 
11458    strcpy(client_program, strchr(net_buffer, ' ') + 1);
11459 
11460    /* get the name of the remote host */
11461 #ifdef OS_VXWORKS
11462    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
11463    if (status != 0)
11464       strcpy(host_name, "unknown");
11465 #else
11466    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
11467    if (phe == NULL)
11468       strcpy(host_name, "unknown");
11469    else
11470       strcpy(host_name, phe->h_name);
11471 #endif
11472 
11473    /* save information in _server_acception structure */
11474    _server_acception[index].recv_sock = recv_sock;
11475    _server_acception[index].send_sock = send_sock;
11476    _server_acception[index].event_sock = event_sock;
11477    _server_acception[index].remote_hw_type = client_hw_type;
11478    strcpy(_server_acception[index].host_name, host_name);
11479    strcpy(_server_acception[index].prog_name, client_program);
11480    _server_acception[index].tid = ss_gettid();
11481    _server_acception[index].last_activity = ss_millitime();
11482    _server_acception[index].watchdog_timeout = 0;
11483 
11484    /* send my own computer id */
11485    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11486    sprintf(str, "%d", hw_type);
11487    send(recv_sock, str, strlen(str) + 1, 0);
11488 
11489    rpc_set_server_acception(index + 1);
11490    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11491    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11492 
11493    /* set callback function for ss_suspend */
11494    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11495                            (int (*)(void)) rpc_server_receive);
11496 
11497    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11498       rpc_debug_printf("Connection to %s:%s established\n",
11499                        _server_acception[index].host_name,
11500                        _server_acception[index].prog_name);
11501 
11502    return RPC_SUCCESS;
11503 
11504  error:
11505 
11506    closesocket(recv_sock);
11507    closesocket(send_sock);
11508    closesocket(event_sock);
11509 
11510    return RPC_NET_ERROR;
11511 }
11512 
11513 
11514 /********************************************************************/
11515 INT rpc_server_thread(void *pointer)
11516 /********************************************************************\
11517 
11518   Routine: rpc_server_thread
11519 
11520   Purpose: New thread for a multi-threaded server. Callback to the
11521            client and process RPC requests.
11522 
11523   Input:
11524     vcoid  pointer          pointer to callback_addr structure.
11525 
11526   Output:
11527     none
11528 
11529   Function value:
11530     RPC_SUCCESS             Successful completion
11531 
11532 \********************************************************************/
11533 {
11534    struct callback_addr callback;
11535    int status, mutex_alarm, mutex_elog;
11536    static DWORD last_checked = 0;
11537 
11538    memcpy(&callback, pointer, sizeof(callback));
11539 
11540    status = rpc_server_callback(&callback);
11541 
11542    if (status != RPC_SUCCESS)
11543       return status;
11544 
11545    /* create alarm and elog mutexes */
11546    ss_mutex_create("ALARM", &mutex_alarm);
11547    ss_mutex_create("ELOG", &mutex_elog);
11548    cm_set_experiment_mutex(mutex_alarm, mutex_elog);
11549 
11550    do {
11551       status = ss_suspend(5000, 0);
11552 
11553       if (rpc_check_channels() == RPC_NET_ERROR)
11554          break;
11555 
11556       /* check alarms every 10 seconds */
11557       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
11558          al_check();
11559          last_checked = ss_time();
11560       }
11561 
11562    } while (status != SS_ABORT && status != SS_EXIT);
11563 
11564    /* delete entry in suspend table for this thread */
11565    ss_suspend_exit();
11566 
11567    return RPC_SUCCESS;
11568 }
11569 
11570 
11571 /********************************************************************/
11572 INT rpc_server_receive(INT index, int sock, BOOL check)
11573 /********************************************************************\
11574 
11575   Routine: rpc_server_receive
11576 
11577   Purpose: Receive rpc commands and execute them. Close the connection
11578            if client has broken TCP pipe.
11579 
11580   Input:
11581     INT    index            Index to _server_acception structure in-
11582                             dicating which connection got data.
11583     int    sock             Socket which got data
11584     BOOL   check            If TRUE, only check if connection is
11585                             broken. This may be called via
11586                             bm_receive_event/ss_suspend(..,MSG_BM)
11587 
11588   Output:
11589     none
11590 
11591   Function value:
11592     RPC_SUCCESS             Successful completion
11593     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
11594     SS_EXIT                 Server connection was closed
11595     SS_ABORT                Server connection was broken
11596 
11597 \********************************************************************/
11598 {
11599    INT status, n_received;
11600    INT remaining, *pbh, start_time;
11601    char test_buffer[256], str[80];
11602    EVENT_HEADER *pevent;
11603 
11604    /* init network buffer */
11605    if (_net_recv_buffer_size == 0) {
11606       _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
11607       if (_net_recv_buffer == NULL) {
11608          cm_msg(MERROR, "rpc_server_receive",
11609                 "not enough memory to allocate network buffer");
11610          return RPC_EXCEED_BUFFER;
11611       }
11612       _net_recv_buffer_size = NET_BUFFER_SIZE;
11613    }
11614 
11615    /* only check if TCP connection is broken */
11616    if (check) {
11617       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
11618 
11619       if (n_received == -1)
11620          cm_msg(MERROR, "rpc_server_receive",
11621                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
11622                 n_received, errno, strerror(errno));
11623 
11624       if (n_received <= 0)
11625          return SS_ABORT;
11626 
11627       return SS_SUCCESS;
11628    }
11629 
11630    remaining = 0;
11631 
11632    /* receive command */
11633    if (sock == _server_acception[index].recv_sock) {
11634       do {
11635          if (_server_acception[index].remote_hw_type == DR_ASCII)
11636             n_received = recv_string(_server_acception[index].recv_sock, _net_recv_buffer,
11637                                      NET_BUFFER_SIZE, 10000);
11638          else
11639             n_received =
11640                 recv_tcp_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
11641 
11642          if (n_received <= 0) {
11643             status = SS_ABORT;
11644             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort",
11645                    n_received);
11646             goto error;
11647          }
11648 
11649          rpc_set_server_acception(index + 1);
11650 
11651          if (_server_acception[index].remote_hw_type == DR_ASCII)
11652             status = rpc_execute_ascii(_server_acception[index].recv_sock,
11653                                        _net_recv_buffer);
11654          else
11655             status = rpc_execute(_server_acception[index].recv_sock,
11656                                  _net_recv_buffer,
11657                                  _server_acception[index].convert_flags);
11658 
11659          if (status == SS_ABORT) {
11660             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort",
11661                    status);
11662             goto error;
11663          }
11664 
11665          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
11666             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11667                rpc_debug_printf("Connection to %s:%s closed\n",
11668                                 _server_acception[index].host_name,
11669                                 _server_acception[index].prog_name);
11670             goto exit;
11671          }
11672 
11673       } while (remaining);
11674    } else {
11675       /* receive event */
11676       if (sock == _server_acception[index].event_sock) {
11677          start_time = ss_millitime();
11678 
11679          do {
11680             n_received =
11681                 recv_event_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0,
11682                                   &remaining);
11683 
11684             if (n_received <= 0) {
11685                status = SS_ABORT;
11686                cm_msg(MERROR, "rpc_server_receive",
11687                       "recv_event_server() returned %d, abort", n_received);
11688                goto error;
11689             }
11690 
11691             /* send event to buffer */
11692             pbh = (INT *) _net_recv_buffer;
11693             pevent = (EVENT_HEADER *) (pbh + 1);
11694 
11695             status =
11696                 bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER),
11697                               SYNC);
11698             if (status != BM_SUCCESS)
11699                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d",
11700                       status);
11701 
11702             /* repeat for maximum 0.5 sec */
11703          } while (ss_millitime() - start_time < 500 && remaining);
11704       }
11705    }
11706 
11707    return RPC_SUCCESS;
11708 
11709  error:
11710 
11711    strcpy(str, _server_acception[index].host_name);
11712    if (strchr(str, '.'))
11713       *strchr(str, '.') = 0;
11714    cm_msg(MTALK, "rpc_server_receive", "Program %s on host %s aborted",
11715           _server_acception[index].prog_name, str);
11716 
11717  exit:
11718 
11719    /* disconnect from experiment as MIDAS server */
11720    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
11721       HNDLE hDB, hKey;
11722 
11723       cm_get_experiment_database(&hDB, &hKey);
11724 
11725       /* only disconnect from experiment if previously connected.
11726          Necessary for pure RPC servers (RPC_SRVR) */
11727       if (hDB) {
11728 #ifdef LOCAL_ROUTINES
11729          ss_alarm(0, cm_watchdog);
11730 #endif
11731 
11732          cm_delete_client_info(hDB, 0);
11733 
11734          bm_close_all_buffers();
11735          db_close_all_databases();
11736 
11737          rpc_deregister_functions();
11738 
11739          cm_set_experiment_database(0, 0);
11740 
11741          _msg_buffer = 0;
11742       }
11743    }
11744 
11745    /* close server connection */
11746    if (_server_acception[index].recv_sock)
11747       closesocket(_server_acception[index].recv_sock);
11748    if (_server_acception[index].send_sock)
11749       closesocket(_server_acception[index].send_sock);
11750    if (_server_acception[index].event_sock)
11751       closesocket(_server_acception[index].event_sock);
11752 
11753    /* free TCP cache */
11754    M_FREE(_server_acception[index].net_buffer);
11755    _server_acception[index].net_buffer = NULL;
11756 
11757    /* mark this entry as invalid */
11758    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
11759 
11760    /* signal caller a shutdonw */
11761    if (status == RPC_SHUTDOWN)
11762       return status;
11763 
11764    /* don't abort if other than main connection is broken */
11765    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11766       return SS_SUCCESS;
11767 
11768    return status;
11769 }
11770 
11771 
11772 /********************************************************************/
11773 INT rpc_server_shutdown(void)
11774 /********************************************************************\
11775 
11776   Routine: rpc_server_shutdown
11777 
11778   Purpose: Shutdown RPC server, abort all connections
11779 
11780   Input:
11781     none
11782 
11783   Output:
11784     none
11785 
11786   Function value:
11787     RPC_SUCCESS             Successful completion
11788 
11789 \********************************************************************/
11790 {
11791    INT i;
11792    struct linger ling;
11793 
11794    /* close all open connections */
11795    for (i = 0; i < MAX_RPC_CONNECTION; i++)
11796       if (_server_acception[i].recv_sock != 0) {
11797          /* lingering needed for PCTCP */
11798          ling.l_onoff = 1;
11799          ling.l_linger = 0;
11800          setsockopt(_server_acception[i].recv_sock,
11801                     SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11802          closesocket(_server_acception[i].recv_sock);
11803 
11804          if (_server_acception[i].send_sock) {
11805             setsockopt(_server_acception[i].send_sock,
11806                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11807             closesocket(_server_acception[i].send_sock);
11808          }
11809 
11810          if (_server_acception[i].event_sock) {
11811             setsockopt(_server_acception[i].event_sock,
11812                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11813             closesocket(_server_acception[i].event_sock);
11814          }
11815 
11816          _server_acception[i].recv_sock = 0;
11817          _server_acception[i].send_sock = 0;
11818          _server_acception[i].event_sock = 0;
11819       }
11820 
11821    if (_lsock) {
11822       closesocket(_lsock);
11823       _lsock = 0;
11824       _server_registered = FALSE;
11825    }
11826 
11827    /* free suspend structures */
11828    ss_suspend_exit();
11829 
11830    return RPC_SUCCESS;
11831 }
11832 
11833 
11834 /********************************************************************/
11835 INT rpc_check_channels(void)
11836 /********************************************************************\
11837 
11838   Routine: rpc_check_channels
11839 
11840   Purpose: Check open rpc channels by sending watchdog messages
11841 
11842   Input:
11843     none
11844 
11845   Output:
11846     none
11847 
11848   Function value:
11849     RPC_SUCCESS             Channel is still alive
11850     RPC_NET_ERROR           Connection is broken
11851 
11852 \********************************************************************/
11853 {
11854    INT status, index, i, convert_flags;
11855    NET_COMMAND nc;
11856    fd_set readfds;
11857    struct timeval timeout;
11858 
11859    for (index = 0; index < MAX_RPC_CONNECTION; index++) {
11860       if (_server_acception[index].recv_sock &&
11861           _server_acception[index].tid == ss_gettid() &&
11862           _server_acception[index].watchdog_timeout &&
11863           (ss_millitime() - _server_acception[index].last_activity >
11864            (DWORD) _server_acception[index].watchdog_timeout)) {
11865 /* printf("Send watchdog message to %s on %s\n",
11866                 _server_acception[index].prog_name,
11867                 _server_acception[index].host_name); */
11868 
11869          /* send a watchdog message */
11870          nc.header.routine_id = MSG_WATCHDOG;
11871          nc.header.param_size = 0;
11872 
11873          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
11874          if (convert_flags) {
11875             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING,
11876                                convert_flags);
11877             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING,
11878                                convert_flags);
11879          }
11880 
11881          /* send the header to the client */
11882          i = send_tcp(_server_acception[index].send_sock,
11883                       (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
11884 
11885          if (i < 0)
11886             goto exit;
11887 
11888          /* make some timeout checking */
11889          FD_ZERO(&readfds);
11890          FD_SET(_server_acception[index].send_sock, &readfds);
11891          FD_SET(_server_acception[index].recv_sock, &readfds);
11892 
11893          timeout.tv_sec = _server_acception[index].watchdog_timeout / 1000;
11894          timeout.tv_usec = (_server_acception[index].watchdog_timeout % 1000) * 1000;
11895 
11896          do {
11897             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
11898 
11899             /* if an alarm signal was cought, restart select with reduced timeout */
11900             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
11901                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
11902 
11903          } while (status == -1);        /* dont return if an alarm signal was cought */
11904 
11905          if (!FD_ISSET(_server_acception[index].send_sock, &readfds) &&
11906              !FD_ISSET(_server_acception[index].recv_sock, &readfds))
11907             goto exit;
11908 
11909          /* receive result on send socket */
11910          if (FD_ISSET(_server_acception[index].send_sock, &readfds)) {
11911             i = recv_tcp(_server_acception[index].send_sock, (char *) &nc, sizeof(nc), 0);
11912             if (i <= 0)
11913                goto exit;
11914          }
11915       }
11916    }
11917 
11918    return RPC_SUCCESS;
11919 
11920  exit:
11921 
11922    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
11923           _server_acception[index].host_name,
11924           _server_acception[index].prog_name,
11925           _server_acception[index].watchdog_timeout / 1000);
11926 
11927    /* disconnect from experiment */
11928    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11929       cm_disconnect_experiment();
11930 
11931    /* close server connection */
11932    if (_server_acception[index].recv_sock)
11933       closesocket(_server_acception[index].recv_sock);
11934    if (_server_acception[index].send_sock)
11935       closesocket(_server_acception[index].send_sock);
11936    if (_server_acception[index].event_sock)
11937       closesocket(_server_acception[index].event_sock);
11938 
11939    /* free TCP cache */
11940    M_FREE(_server_acception[index].net_buffer);
11941    _server_acception[index].net_buffer = NULL;
11942 
11943    /* mark this entry as invalid */
11944    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
11945 
11946    return RPC_NET_ERROR;
11947 }
11948 
11949 /**dox***************************************************************/
11950 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
11951 
11952 /**dox***************************************************************/
11953                    /** @} *//* end of rpcfunctionc */
11954 
11955 /**dox***************************************************************/
11956 /** @addtogroup bkfunctionc
11957  *  
11958  *  @{  */
11959 
11960 /********************************************************************\
11961 *                                                                    *
11962 *                 Bank functions                                     *
11963 *                                                                    *
11964 \********************************************************************/
11965 
11966 /********************************************************************/
11967 /**
11968 Initializes an event for Midas banks structure.
11969 Before banks can be created in an event, bk_init() has to be called first.
11970 @param event pointer to the area of event
11971 */
11972 void bk_init(void *event)
11973 {
11974    ((BANK_HEADER *) event)->data_size = 0;
11975    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
11976 }
11977 
11978 /**dox***************************************************************/
11979 #ifndef DOXYGEN_SHOULD_SKIP_THIS
11980 
11981 /********************************************************************/
11982 BOOL bk_is32(void *event)
11983 /********************************************************************\
11984 
11985   Routine: bk_is32
11986 
11987   Purpose: Return true if banks inside event are 32-bit banks
11988 
11989   Input:
11990     void   *event           pointer to the event
11991 
11992   Output:
11993     none
11994 
11995   Function value:
11996     none
11997 
11998 \********************************************************************/
11999 {
12000    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12001 }
12002 
12003 /**dox***************************************************************/
12004 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12005 
12006 /********************************************************************/
12007 /**
12008 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12009 Before banks can be created in an event, bk_init32() has to be called first.
12010 @param event pointer to the area of event
12011 @return void
12012 */
12013 void bk_init32(void *event)
12014 {
12015    ((BANK_HEADER *) event)->data_size = 0;
12016    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12017 }
12018 
12019 /********************************************************************/
12020 /**
12021 Returns the size of an event containing banks.
12022 The total size of an event is the value returned by bk_size() plus the size
12023 of the event header (sizeof(EVENT_HEADER)).
12024 @param event pointer to the area of event
12025 @return number of bytes contained in data area of event
12026 */
12027 INT bk_size(void *event)
12028 {
12029    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12030 }
12031 
12032 /********************************************************************/
12033 /**
12034 Create a Midas bank.
12035 The data pointer pdata must be used as an address to fill a
12036 bank. It is incremented with every value written to the bank and finally points
12037 to a location just after the last byte of the bank. It is then passed to
12038 the function bk_close() to finish the bank creation.
12039 \code
12040 INT *pdata;
12041 bk_init(pevent);
12042 bk_create(pevent, "ADC0", TID_INT, &pdata);
12043 *pdata++ = 123
12044 *pdata++ = 456
12045 bk_close(pevent, pdata);
12046 \endcode
12047 @param event pointer to the data area
12048 @param name of the bank, must be exactly 4 charaters
12049 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12050 midas.h
12051 @param pdata pointer to the data area of the newly created bank
12052 @return void
12053 */
12054 void bk_create(void *event, const char *name, WORD type, void *pdata)
12055 {
12056    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12057       BANK32 *pbk32;
12058 
12059       pbk32 =
12060           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12061                       ((BANK_HEADER *) event)->data_size);
12062       strncpy(pbk32->name, name, 4);
12063       pbk32->type = type;
12064       pbk32->data_size = 0;
12065       *((void **) pdata) = pbk32 + 1;
12066    } else {
12067       BANK *pbk;
12068 
12069       pbk =
12070           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12071                     ((BANK_HEADER *) event)->data_size);
12072       strncpy(pbk->name, name, 4);
12073       pbk->type = type;
12074       pbk->data_size = 0;
12075       *((void **) pdata) = pbk + 1;
12076    }
12077 }
12078 
12079 
12080 
12081 /**dox***************************************************************/
12082 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12083 
12084 /********************************************************************/
12085 int bk_delete(void *event, const char *name)
12086 /********************************************************************\
12087 
12088   Routine: bk_delete
12089 
12090   Purpose: Delete a MIDAS bank inside an event
12091 
12092   Input:
12093     void   *event           pointer to the event
12094     char   *name            Name of bank (exactly four letters)
12095 
12096   Function value:
12097     CM_SUCCESS              Bank has been deleted
12098     0                       Bank has not been found
12099 
12100 \********************************************************************/
12101 {
12102    BANK *pbk;
12103    BANK32 *pbk32;
12104    DWORD dname;
12105    int remaining;
12106 
12107    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12108       /* locate bank */
12109       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12110       strncpy((char *) &dname, name, 4);
12111       do {
12112          if (*((DWORD *) pbk32->name) == dname) {
12113             /* bank found, delete it */
12114             remaining =
12115                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12116                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk32 + 1) +
12117                                                      ALIGN8(pbk32->data_size));
12118 
12119             /* reduce total event size */
12120             ((BANK_HEADER *) event)->data_size -=
12121                 sizeof(BANK32) + ALIGN8(pbk32->data_size);
12122 
12123             /* copy remaining bytes */
12124             if (remaining > 0)
12125                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12126             return CM_SUCCESS;
12127          }
12128 
12129          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12130       } while ((DWORD) pbk32 - (DWORD) event <
12131                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12132    } else {
12133       /* locate bank */
12134       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12135       strncpy((char *) &dname, name, 4);
12136       do {
12137          if (*((DWORD *) pbk->name) == dname) {
12138             /* bank found, delete it */
12139             remaining =
12140                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12141                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk + 1) +
12142                                                      ALIGN8(pbk->data_size));
12143 
12144             /* reduce total event size */
12145             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
12146 
12147             /* copy remaining bytes */
12148             if (remaining > 0)
12149                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
12150             return CM_SUCCESS;
12151          }
12152 
12153          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12154       } while ((DWORD) pbk - (DWORD) event <
12155                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12156    }
12157 
12158    return 0;
12159 }
12160 
12161 /**dox***************************************************************/
12162 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12163 
12164 /********************************************************************/
12165 /**
12166 Close the Midas bank priviously created by bk_create().
12167 The data pointer pdata must be obtained by bk_create() and
12168 used as an address to fill a bank. It is incremented with every value written
12169 to the bank and finally points to a location just after the last byte of the
12170 bank. It is then passed to bk_close() to finish the bank creation
12171 @param event pointer to current composed event
12172 @param pdata  pointer to the data
12173 @return number of bytes contained in bank
12174 */
12175 INT bk_close(void *event, void *pdata)
12176 {
12177    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12178       BANK32 *pbk32;
12179 
12180       pbk32 =
12181           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12182                       ((BANK_HEADER *) event)->data_size);
12183       pbk32->data_size = (DWORD) (INT) pdata - (INT) (pbk32 + 1);
12184       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12185          printf("Warning: bank %c%c%c%c has zero size\n",
12186                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
12187       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
12188       return pbk32->data_size;
12189    } else {
12190       BANK *pbk;
12191 
12192       pbk =
12193           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12194                     ((BANK_HEADER *) event)->data_size);
12195       pbk->data_size = (WORD) (INT) pdata - (INT) (pbk + 1);
12196       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12197          printf("Warning: bank %c%c%c%c has zero size\n",
12198                 pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12199       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
12200       return pbk->data_size;
12201    }
12202 }
12203 
12204 /********************************************************************/
12205 /**
12206 Extract the MIDAS bank name listing of an event.
12207 The bklist should be dimensioned with STRING_BANKLIST_MAX
12208 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12209 \code
12210 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12211 {
12212   INT    n_adc, nbanks;
12213   WORD   *pdata;
12214   char   banklist[STRING_BANKLIST_MAX];
12215 
12216   // Display # of banks and list of banks in the event
12217   nbanks = bk_list(pevent, banklist);
12218   printf("#banks:%d List:%s\n", nbanks, banklist);
12219 
12220   // look for ADC0 bank, return if not present
12221   n_adc = bk_locate(pevent, "ADC0", &pdata);
12222   ...
12223 }
12224 \endcode
12225 @param event pointer to current composed event
12226 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12227 @return number of bank found in this event.
12228 */
12229 INT bk_list(void *event, char *bklist)
12230 {                               /* Full event */
12231    INT nbk, size;
12232    BANK *pmbk = NULL;
12233    BANK32 *pmbk32 = NULL;
12234    char *pdata;
12235 
12236    /* compose bank list */
12237    bklist[0] = 0;
12238    nbk = 0;
12239    do {
12240       /* scan all banks for bank name only */
12241       if (bk_is32(event)) {
12242          size = bk_iterate32(event, &pmbk32, &pdata);
12243          if (pmbk32 == NULL)
12244             break;
12245       } else {
12246          size = bk_iterate(event, &pmbk, &pdata);
12247          if (pmbk == NULL)
12248             break;
12249       }
12250       nbk++;
12251 
12252       if (nbk > BANKLIST_MAX) {
12253          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
12254          return (nbk - 1);
12255       }
12256       if (bk_is32(event))
12257          strncat(bklist, (char *) pmbk32->name, 4);
12258       else
12259          strncat(bklist, (char *) pmbk->name, 4);
12260    }
12261    while (1);
12262    return (nbk);
12263 }
12264 
12265 /********************************************************************/
12266 /**
12267 Locates a MIDAS bank of given name inside an event.
12268 @param event pointer to current composed event
12269 @param name bank name to look for
12270 @param pdata pointer to data area of bank, NULL if bank not found
12271 @return number of values inside the bank
12272 */
12273 INT bk_locate(void *event, const char *name, void *pdata)
12274 {
12275    BANK *pbk;
12276    BANK32 *pbk32;
12277    DWORD dname;
12278 
12279    if (bk_is32(event)) {
12280       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12281       strncpy((char *) &dname, name, 4);
12282       while ((DWORD) pbk32 - (DWORD) event <
12283                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12284          if (*((DWORD *) pbk32->name) == dname) {
12285             *((void **) pdata) = pbk32 + 1;
12286             if (tid_size[pbk32->type & 0xFF] == 0)
12287                return pbk32->data_size;
12288             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
12289          }
12290          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12291       }
12292    } else {
12293       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12294       strncpy((char *) &dname, name, 4);
12295       while ((DWORD) pbk - (DWORD) event <
12296                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12297          if (*((DWORD *) pbk->name) == dname) {
12298             *((void **) pdata) = pbk + 1;
12299             if (tid_size[pbk->type & 0xFF] == 0)
12300                return pbk->data_size;
12301             return pbk->data_size / tid_size[pbk->type & 0xFF];
12302          }
12303          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12304       }
12305 
12306    }
12307 
12308    /* bank not found */
12309    *((void **) pdata) = NULL;
12310    return 0;
12311 }
12312 
12313 /********************************************************************/
12314 /**
12315 Finds a MIDAS bank of given name inside an event.
12316 @param pbkh pointer to current composed event
12317 @param name bank name to look for
12318 @param bklen number of elemtents in bank
12319 @param bktype bank type, one of TID_xxx
12320 @param pdata pointer to data area of bank, NULL if bank not found
12321 @return 1 if bank found, 0 otherwise
12322 */
12323 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype,
12324             void **pdata)
12325 {
12326    BANK *pbk;
12327    BANK32 *pbk32;
12328    DWORD dname;
12329 
12330    if (bk_is32(pbkh)) {
12331       pbk32 = (BANK32 *) (pbkh + 1);
12332       strncpy((char *) &dname, name, 4);
12333       do {
12334          if (*((DWORD *) pbk32->name) == dname) {
12335             *((void **) pdata) = pbk32 + 1;
12336             if (tid_size[pbk32->type & 0xFF] == 0)
12337                *bklen = pbk32->data_size;
12338             else
12339                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
12340 
12341             *bktype = pbk32->type;
12342             return 1;
12343          }
12344          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12345       } while ((DWORD) pbk32 - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
12346    } else {
12347       pbk = (BANK *) (pbkh + 1);
12348       strncpy((char *) &dname, name, 4);
12349       do {
12350          if (*((DWORD *) pbk->name) == dname) {
12351             *((void **) pdata) = pbk + 1;
12352             if (tid_size[pbk->type & 0xFF] == 0)
12353                *bklen = pbk->data_size;
12354             else
12355                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
12356 
12357             *bktype = pbk->type;
12358             return 1;
12359          }
12360          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12361       } while ((DWORD) pbk - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
12362    }
12363 
12364    /* bank not found */
12365    *((void **) pdata) = NULL;
12366    return 0;
12367 }
12368 
12369 /********************************************************************/
12370 /**
12371 Iterates through banks inside an event.
12372 The function can be used to enumerate all banks of an event.
12373 The returned pointer to the bank header has following structure:
12374 \code
12375 typedef struct {
12376 char   name[4];
12377 WORD   type;
12378 WORD   data_size;
12379 } BANK;
12380 \endcode
12381 where type is a TID_xxx value and data_size the size of the bank in bytes.
12382 \code
12383 BANK *pbk;
12384 INT  size;
12385 void *pdata;
12386 char name[5];
12387 pbk = NULL;
12388 do
12389 {
12390  size = bk_iterate(event, &pbk, &pdata);
12391  if (pbk == NULL)
12392   break;
12393  *((DWORD *)name) = *((DWORD *)(pbk)->name);
12394  name[4] = 0;
12395  printf("bank %s found\n", name);
12396 } while(TRUE);
12397 \endcode
12398 @param event Pointer to data area of event.
12399 @param pbk pointer to the bank header, must be NULL for the first call to
12400 this function.
12401 @param pdata Pointer to the bank header, must be NULL for the first
12402 call to this function
12403 @return Size of bank in bytes
12404 */
12405 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
12406 {
12407    if (*pbk == NULL)
12408       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12409    else
12410       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12411 
12412    *((void **) pdata) = (*pbk) + 1;
12413 
12414    if ((DWORD) * pbk - (DWORD) event >=
12415        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12416       *pbk = *((BANK **) pdata) = NULL;
12417       return 0;
12418    }
12419 
12420    return (*pbk)->data_size;
12421 }
12422 
12423 
12424 /**dox***************************************************************/
12425 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12426 
12427 /********************************************************************/
12428 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
12429 /********************************************************************\
12430 
12431   Routine: bk_iterate
12432 
12433   Purpose: Iterate through 32 bit MIDAS banks inside an event
12434 
12435   Input:
12436     void   *event           pointer to the event
12437     BANK   **pbk32          must be NULL for the first call to bk_iterate
12438 
12439   Output:
12440     BANK   **pbk            pointer to the bank header
12441     void   *pdata           pointer to data area of the bank
12442 
12443   Function value:
12444     INT    size of the bank in bytes
12445 
12446 \********************************************************************/
12447 {
12448    if (*pbk == NULL)
12449       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
12450    else
12451       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12452 
12453    *((void **) pdata) = (*pbk) + 1;
12454 
12455    if ((DWORD) * pbk - (DWORD) event >=
12456        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12457       *pbk = *((BANK32 **) pdata) = NULL;
12458       return 0;
12459    }
12460 
12461    return (*pbk)->data_size;
12462 }
12463 
12464 /**dox***************************************************************/
12465 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12466 
12467 /********************************************************************/
12468 /**
12469 Swaps bytes from little endian to big endian or vice versa for a whole event.
12470 
12471 An event contains a flag which is set by bk_init() to identify
12472 the endian format of an event. If force is FALSE, this flag is evaluated and
12473 the event is only swapped if it is in the "wrong" format for this system.
12474 An event can be swapped to the "wrong" format on purpose for example by a
12475 front-end which wants to produce events in a "right" format for a back-end
12476 analyzer which has different byte ordering.
12477 @param event pointer to data area of event
12478 @param force If TRUE, the event is always swapped, if FALSE, the event
12479 is only swapped if it is in the wrong format.
12480 @return 1==event has been swap, 0==event has not been swapped.
12481 */
12482 INT bk_swap(void *event, BOOL force)
12483 {
12484    BANK_HEADER *pbh;
12485    BANK *pbk;
12486    BANK32 *pbk32;
12487    void *pdata;
12488    WORD type;
12489    BOOL b32;
12490 
12491    pbh = (BANK_HEADER *) event;
12492 
12493    /* only swap if flags in high 16-bit */
12494    if (pbh->flags < 0x10000 && !force)
12495       return 0;
12496 
12497    /* swap bank header */
12498    DWORD_SWAP(&pbh->data_size);
12499    DWORD_SWAP(&pbh->flags);
12500 
12501    /* check for 32bit banks */
12502    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
12503 
12504    pbk = (BANK *) (pbh + 1);
12505    pbk32 = (BANK32 *) pbk;
12506 
12507    /* scan event */
12508    while ((PTYPE) pbk - (PTYPE) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
12509       /* swap bank header */
12510       if (b32) {
12511          DWORD_SWAP(&pbk32->type);
12512          DWORD_SWAP(&pbk32->data_size);
12513          pdata = pbk32 + 1;
12514          type = (WORD) pbk32->type;
12515       } else {
12516          WORD_SWAP(&pbk->type);
12517          WORD_SWAP(&pbk->data_size);
12518          pdata = pbk + 1;
12519          type = pbk->type;
12520       }
12521 
12522       /* pbk points to next bank */
12523       if (b32) {
12524          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12525          pbk = (BANK *) pbk32;
12526       } else {
12527          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12528          pbk32 = (BANK32 *) pbk;
12529       }
12530 
12531       switch (type) {
12532       case TID_WORD:
12533       case TID_SHORT:
12534          while ((PTYPE) pdata < (PTYPE) pbk) {
12535             WORD_SWAP(pdata);
12536             pdata = (void *) (((WORD *) pdata) + 1);
12537          }
12538          break;
12539 
12540       case TID_DWORD:
12541       case TID_INT:
12542       case TID_BOOL:
12543       case TID_FLOAT:
12544          while ((PTYPE) pdata < (PTYPE) pbk) {
12545             DWORD_SWAP(pdata);
12546             pdata = (void *) (((DWORD *) pdata) + 1);
12547          }
12548          break;
12549 
12550       case TID_DOUBLE:
12551          while ((PTYPE) pdata < (PTYPE) pbk) {
12552             QWORD_SWAP(pdata);
12553             pdata = (void *) (((double *) pdata) + 1);
12554          }
12555          break;
12556       }
12557    }
12558 
12559    return CM_SUCCESS;
12560 }
12561 
12562 /**dox***************************************************************/
12563                    /** @} *//* end of bkfunctionc */
12564 
12565 /**dox***************************************************************/
12566 /** @addtogroup hsfunctionc
12567  *  
12568  *  @{  */
12569 
12570 #if !defined(OS_VXWORKS)
12571 /********************************************************************\
12572 *                                                                    *
12573 *                 History functions                                  *
12574 *                                                                    *
12575 \********************************************************************/
12576 
12577 /**dox***************************************************************/
12578 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12579 
12580 static HISTORY *_history;
12581 static INT _history_entries = 0;
12582 static char _hs_path_name[MAX_STRING_LENGTH];
12583 
12584 /**dox***************************************************************/
12585 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12586 
12587 /********************************************************************/
12588 /**
12589 Sets the path for future history file accesses. Should
12590 be called before any other history function is called.
12591 @param path             Directory where history files reside
12592 @return HS_SUCCESS
12593 */
12594 INT hs_set_path(char *path)
12595 {
12596    /* set path locally and remotely */
12597    if (rpc_is_remote())
12598       rpc_call(RPC_HS_SET_PATH, path);
12599 
12600    strcpy(_hs_path_name, path);
12601 
12602    /* check for trailing directory seperator */
12603    if (strlen(_hs_path_name) > 0 &&
12604        _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
12605       strcat(_hs_path_name, DIR_SEPARATOR_STR);
12606 
12607    return HS_SUCCESS;
12608 }
12609 
12610 
12611 /********************************************************************/
12612 /**
12613 Open history file belonging to certain date. Internal use
12614            only.
12615 @param ltime          Date for which a history file should be opened.
12616 @param suffix         File name suffix like "hst", "idx", "idf"
12617 @param mode           R/W access mode
12618 @param fh             File handle
12619 @return HS_SUCCESS
12620 */
12621 INT hs_open_file(DWORD ltime, char *suffix, INT mode, int *fh)
12622 {
12623    struct tm *tms;
12624    char file_name[256];
12625 
12626    /* generate new file name YYMMDD.xxx */
12627 #if !defined(OS_VXWORKS)
12628 #if !defined(OS_VMS)
12629    tzset();
12630 #endif
12631 #endif
12632    tms = localtime((const time_t *) &ltime);
12633 
12634    sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name,
12635            tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
12636 
12637    /* open file, add O_BINARY flag for Windows NT */
12638    *fh = open(file_name, mode | O_BINARY, 0644);
12639 
12640    return HS_SUCCESS;
12641 }
12642 
12643 /**dox***************************************************************/
12644 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12645 
12646 /********************************************************************/
12647 INT hs_gen_index(DWORD ltime)
12648 /********************************************************************\
12649 
12650   Routine: hs_gen_index
12651 
12652   Purpose: Regenerate index files ("idx" and "idf" files) for a given
12653            history file ("hst"). Interal use only.
12654 
12655   Input:
12656     DWORD  ltime            Date for which a history file should
12657                             be analyzed.
12658 
12659   Output:
12660     none
12661 
12662   Function value:
12663     HS_SUCCESS              Successful completion
12664     HS_FILE_ERROR           Index files cannot be created
12665 
12666 \********************************************************************/
12667 {
12668    char event_name[NAME_LENGTH];
12669    int fh, fhd, fhi;
12670    INT n;
12671    HIST_RECORD rec;
12672    INDEX_RECORD irec;
12673    DEF_RECORD def_rec;
12674 
12675    printf("Recovering index files...\n");
12676 
12677    if (ltime == 0)
12678       ltime = time(NULL);
12679 
12680    /* open new index file */
12681    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
12682    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
12683 
12684    if (fhd < 0 || fhi < 0) {
12685       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
12686       return HS_FILE_ERROR;
12687    }
12688 
12689    /* open history file */
12690    hs_open_file(ltime, "hst", O_RDONLY, &fh);
12691    if (fh < 0)
12692       return HS_FILE_ERROR;
12693    lseek(fh, 0, SEEK_SET);
12694 
12695    /* loop over file records in .hst file */
12696    do {
12697       n = read(fh, (char *) &rec, sizeof(rec));
12698       if (n < sizeof(rec))
12699          break;
12700 
12701       /* check if record type is definition */
12702       if (rec.record_type == RT_DEF) {
12703          /* read name */
12704          read(fh, event_name, sizeof(event_name));
12705 
12706          printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
12707 
12708          /* write definition index record */
12709          def_rec.event_id = rec.event_id;
12710          memcpy(def_rec.event_name, event_name, sizeof(event_name));
12711          def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
12712          write(fhd, (char *) &def_rec, sizeof(def_rec));
12713 
12714          /* skip tags */
12715          lseek(fh, rec.data_size, SEEK_CUR);
12716       } else {
12717          /* write index record */
12718          irec.event_id = rec.event_id;
12719          irec.time = rec.time;
12720          irec.offset = TELL(fh) - sizeof(rec);
12721          write(fhi, (char *) &irec, sizeof(irec));
12722 
12723          /* printf("ID %d, %s\n", rec.event_id, ctime((const time_t *)&irec.time)+4); */
12724 
12725          /* skip data */
12726          lseek(fh, rec.data_size, SEEK_CUR);
12727       }
12728 
12729    } while (TRUE);
12730 
12731    close(fh);
12732    close(fhi);
12733    close(fhd);
12734 
12735    printf("...done.\n");
12736 
12737    return HS_SUCCESS;
12738 }
12739 
12740 
12741 /********************************************************************/
12742 INT hs_search_file(DWORD * ltime, INT direction)
12743 /********************************************************************\
12744 
12745   Routine: hs_search_file
12746 
12747   Purpose: Search an history file for a given date. If not found,
12748            look for files after date (direction==1) or before date
12749            (direction==-1) up to one year.
12750 
12751   Input:
12752     DWORD  *ltime           Date of history file
12753     INT    direction        Search direction
12754 
12755   Output:
12756     DWORD  *ltime           Date of history file found
12757 
12758   Function value:
12759     HS_SUCCESS              Successful completion
12760     HS_FILE_ERROR           No file found
12761 
12762 \********************************************************************/
12763 {
12764    DWORD lt;
12765    int fh, fhd, fhi;
12766    struct tm *tms;
12767 
12768    if (*ltime == 0)
12769       *ltime = time(NULL);
12770 
12771    lt = *ltime;
12772    do {
12773       /* try to open history file for date "lt" */
12774       hs_open_file(lt, "hst", O_RDONLY, &fh);
12775 
12776       /* if not found, look for next day */
12777       if (fh < 0)
12778          lt += direction * 3600 * 24;
12779 
12780       /* stop if more than a year before starting point or in the future */
12781    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 &&
12782             lt < (DWORD) time(NULL));
12783 
12784    if (fh < 0)
12785       return HS_FILE_ERROR;
12786 
12787    if (lt != *ltime) {
12788       /* if switched to new day, set start_time to 0:00 */
12789       tms = localtime((const time_t *) &lt);
12790       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
12791       *ltime = mktime(tms);
12792    }
12793 
12794    /* check if index files are there */
12795    hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
12796    hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
12797 
12798    close(fh);
12799    close(fhd);
12800    close(fhi);
12801 
12802    /* generate them if not */
12803    if (fhd < 0 || fhi < 0)
12804       hs_gen_index(*ltime);
12805 
12806    return HS_SUCCESS;
12807 }
12808 
12809 
12810 /********************************************************************/
12811 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
12812 /********************************************************************\
12813 
12814   Routine: hs_define_evnet
12815 
12816   Purpose: Define a new event for which a history should be recorded.
12817            This routine must be called before any call to
12818            hs_write_event. It also should be called if the definition
12819            of the event has changed.
12820 
12821            The event definition is written directly to the history
12822            file. If the definition is identical to a previous
12823            definition, it is not written to the file.
12824 
12825 
12826   Input:
12827     DWORD  event_id         ID for this event. Must be unique.
12828     char   name             Name of this event
12829     TAG    tag              Tag list containing names and types of
12830                             variables in this event.
12831     DWORD  size             Size of tag array
12832 
12833   Output:
12834     <none>
12835 
12836   Function value:
12837     HS_SUCCESS              Successful completion
12838     HS_NO_MEMEORY           Out of memory
12839     HS_FILE_ERROR           Cannot open history file
12840 
12841 \********************************************************************/
12842 {
12843 /* History events are only written locally (?)
12844 
12845   if (rpc_is_remote())
12846     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
12847 */
12848    {
12849       HIST_RECORD rec, prev_rec;
12850       DEF_RECORD def_rec;
12851       char str[256], event_name[NAME_LENGTH], *buffer;
12852       int fh, fhi, fhd;
12853       INT i, n, len, index;
12854       struct tm *tmb;
12855 
12856       /* allocate new space for the new history descriptor */
12857       if (_history_entries == 0) {
12858          _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
12859          memset(_history, 0, sizeof(HISTORY));
12860          if (_history == NULL)
12861             return HS_NO_MEMORY;
12862 
12863          _history_entries = 1;
12864          index = 0;
12865       } else {
12866          /* check if history already open */
12867          for (i = 0; i < _history_entries; i++)
12868             if (_history[i].event_id == event_id)
12869                break;
12870 
12871          /* if not found, create new one */
12872          if (i == _history_entries) {
12873             _history =
12874                 (HISTORY *) realloc(_history, sizeof(HISTORY) * (_history_entries + 1));
12875             memset(&_history[_history_entries], 0, sizeof(HISTORY));
12876 
12877             _history_entries++;
12878             if (_history == NULL) {
12879                _history_entries--;
12880                return HS_NO_MEMORY;
12881             }
12882          }
12883          index = i;
12884       }
12885 
12886       /* assemble definition record header */
12887       rec.record_type = RT_DEF;
12888       rec.event_id = event_id;
12889       rec.time = time(NULL);
12890       rec.data_size = size;
12891       strncpy(event_name, name, NAME_LENGTH);
12892 
12893       /* pad tag names with zeos */
12894       for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
12895          len = strlen(tag[i].name);
12896          memset(tag[i].name + len, 0, NAME_LENGTH - len);
12897       }
12898 
12899       /* if history structure not set up, do so now */
12900       if (!_history[index].hist_fh) {
12901          /* open history file */
12902          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
12903          if (fh < 0)
12904             return HS_FILE_ERROR;
12905 
12906          /* open index files */
12907          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
12908          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
12909          lseek(fh, 0, SEEK_END);
12910          lseek(fhi, 0, SEEK_END);
12911          lseek(fhd, 0, SEEK_END);
12912 
12913          /* regenerate index if missing */
12914          if (TELL(fh) > 0 && TELL(fhd) == 0) {
12915             close(fh);
12916             close(fhi);
12917             close(fhd);
12918             hs_gen_index(rec.time);
12919             hs_open_file(rec.time, "hst", O_RDWR, &fh);
12920             hs_open_file(rec.time, "idx", O_RDWR, &fhi);
12921             hs_open_file(rec.time, "idf", O_RDWR, &fhd);
12922             lseek(fh, 0, SEEK_END);
12923             lseek(fhi, 0, SEEK_END);
12924             lseek(fhd, 0, SEEK_END);
12925          }
12926 
12927          tmb = localtime((const time_t *) &rec.time);
12928          tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
12929 
12930          /* setup history structure */
12931          _history[index].hist_fh = fh;
12932          _history[index].index_fh = fhi;
12933          _history[index].def_fh = fhd;
12934          _history[index].def_offset = TELL(fh);
12935          _history[index].event_id = event_id;
12936          strcpy(_history[index].event_name, event_name);
12937          _history[index].base_time = mktime(tmb);
12938          _history[index].n_tag = size / sizeof(TAG);
12939          _history[index].tag = (TAG *) M_MALLOC(size);
12940          memcpy(_history[index].tag, tag, size);
12941 
12942          /* search previous definition */
12943          n = TELL(fhd) / sizeof(def_rec);
12944          def_rec.event_id = 0;
12945          for (i = n - 1; i >= 0; i--) {
12946             lseek(fhd, i * sizeof(def_rec), SEEK_SET);
12947             read(fhd, (char *) &def_rec, sizeof(def_rec));
12948             if (def_rec.event_id == event_id)
12949                break;
12950          }
12951          lseek(fhd, 0, SEEK_END);
12952 
12953          /* if definition found, compare it with new one */
12954          if (def_rec.event_id == event_id) {
12955             buffer = (char *) M_MALLOC(size);
12956             memset(buffer, 0, size);
12957 
12958             lseek(fh, def_rec.def_offset, SEEK_SET);
12959             read(fh, (char *) &prev_rec, sizeof(prev_rec));
12960             read(fh, str, NAME_LENGTH);
12961             read(fh, buffer, size);
12962             lseek(fh, 0, SEEK_END);
12963 
12964             if (prev_rec.data_size != size ||
12965                 strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
12966                /* write definition to history file */
12967                write(fh, (char *) &rec, sizeof(rec));
12968                write(fh, event_name, NAME_LENGTH);
12969                write(fh, (char *) tag, size);
12970 
12971                /* write index record */
12972                def_rec.event_id = event_id;
12973                memcpy(def_rec.event_name, event_name, sizeof(event_name));
12974                def_rec.def_offset = _history[index].def_offset;
12975                write(fhd, (char *) &def_rec, sizeof(def_rec));
12976             } else
12977                /* definition identical, just remember old offset */
12978                _history[index].def_offset = def_rec.def_offset;
12979 
12980             M_FREE(buffer);
12981          } else {
12982             /* write definition to history file */
12983             write(fh, (char *) &rec, sizeof(rec));
12984             write(fh, event_name, NAME_LENGTH);
12985             write(fh, (char *) tag, size);
12986 
12987             /* write definition index record */
12988             def_rec.event_id = event_id;
12989             memcpy(def_rec.event_name, event_name, sizeof(event_name));
12990             def_rec.def_offset = _history[index].def_offset;
12991             write(fhd, (char *) &def_rec, sizeof(def_rec));
12992          }
12993       } else {
12994          fh = _history[index].hist_fh;
12995          fhd = _history[index].def_fh;
12996 
12997          /* compare definition with previous definition */
12998          buffer = (char *) M_MALLOC(size);
12999          memset(buffer, 0, size);
13000 
13001          lseek(fh, _history[index].def_offset, SEEK_SET);
13002          read(fh, (char *) &prev_rec, sizeof(prev_rec));
13003          read(fh, str, NAME_LENGTH);
13004          read(fh, buffer, size);
13005 
13006          lseek(fh, 0, SEEK_END);
13007          lseek(fhd, 0, SEEK_END);
13008 
13009          if (prev_rec.data_size != size ||
13010              strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13011             /* save new definition offset */
13012             _history[index].def_offset = TELL(fh);
13013 
13014             /* write definition to history file */
13015             write(fh, (char *) &rec, sizeof(rec));
13016             write(fh, event_name, NAME_LENGTH);
13017             write(fh, (char *) tag, size);
13018 
13019             /* write index record */
13020             def_rec.event_id = event_id;
13021             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13022             def_rec.def_offset = _history[index].def_offset;
13023             write(fhd, (char *) &def_rec, sizeof(def_rec));
13024          }
13025 
13026          M_FREE(buffer);
13027       }
13028 
13029    }
13030 
13031    return HS_SUCCESS;
13032 }
13033 
13034 
13035 /********************************************************************/
13036 INT hs_write_event(DWORD event_id, void *data, DWORD size)
13037 /********************************************************************\
13038 
13039   Routine: hs_write_event
13040 
13041   Purpose: Write an event to a history file.
13042 
13043   Input:
13044     DWORD  event_id         Event ID
13045     void   *data            Data buffer containing event
13046     DWORD  size             Data buffer size in bytes
13047 
13048   Output:
13049     none
13050                             future hs_write_event
13051 
13052   Function value:
13053     HS_SUCCESS              Successful completion
13054     HS_NO_MEMEORY           Out of memory
13055     HS_FILE_ERROR           Cannot write to history file
13056     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
13057 
13058 \********************************************************************/
13059 {
13060 /* history events are only written locally (?)
13061 
13062   if (rpc_is_remote())
13063     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
13064 */
13065    HIST_RECORD rec, drec;
13066    DEF_RECORD def_rec;
13067    INDEX_RECORD irec;
13068    int fh, fhi, fhd;
13069    INT index;
13070    struct tm tmb, tmr;
13071 
13072    /* find index to history structure */
13073    for (index = 0; index < _history_entries; index++)
13074       if (_history[index].event_id == event_id)
13075          break;
13076    if (index == _history_entries)
13077       return HS_UNDEFINED_EVENT;
13078 
13079    /* assemble record header */
13080    rec.record_type = RT_DATA;
13081    rec.event_id = _history[index].event_id;
13082    rec.time = time(NULL);
13083    rec.def_offset = _history[index].def_offset;
13084    rec.data_size = size;
13085 
13086    irec.event_id = _history[index].event_id;
13087    irec.time = rec.time;
13088 
13089    /* check if new day */
13090    memcpy(&tmr, localtime((const time_t *) &rec.time), sizeof(tmr));
13091    memcpy(&tmb, localtime((const time_t *) &_history[index].base_time), sizeof(tmb));
13092 
13093    if (tmr.tm_yday != tmb.tm_yday) {
13094       /* close current history file */
13095       close(_history[index].hist_fh);
13096       close(_history[index].def_fh);
13097       close(_history[index].index_fh);
13098 
13099       /* open new history file */
13100       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13101       if (fh < 0)
13102          return HS_FILE_ERROR;
13103 
13104       /* open new index file */
13105       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13106       if (fhi < 0)
13107          return HS_FILE_ERROR;
13108 
13109       /* open new definition index file */
13110       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13111       if (fhd < 0)
13112          return HS_FILE_ERROR;
13113 
13114       lseek(fh, 0, SEEK_END);
13115       lseek(fhi, 0, SEEK_END);
13116       lseek(fhd, 0, SEEK_END);
13117 
13118       /* remember new file handles */
13119       _history[index].hist_fh = fh;
13120       _history[index].index_fh = fhi;
13121       _history[index].def_fh = fhd;
13122 
13123       _history[index].def_offset = TELL(fh);
13124       rec.def_offset = _history[index].def_offset;
13125 
13126       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
13127       _history[index].base_time = mktime(&tmr);
13128 
13129       /* write definition from _history structure */
13130       drec.record_type = RT_DEF;
13131       drec.event_id = _history[index].event_id;
13132       drec.time = rec.time;
13133       drec.data_size = _history[index].n_tag * sizeof(TAG);
13134 
13135       write(fh, (char *) &drec, sizeof(drec));
13136       write(fh, _history[index].event_name, NAME_LENGTH);
13137       write(fh, (char *) _history[index].tag, drec.data_size);
13138 
13139       /* write definition index record */
13140       def_rec.event_id = _history[index].event_id;
13141       memcpy(def_rec.event_name, _history[index].event_name, sizeof(def_rec.event_name));
13142       def_rec.def_offset = _history[index].def_offset;
13143       write(fhd, (char *) &def_rec, sizeof(def_rec));
13144    }
13145 
13146    /* got to end of file */
13147    lseek(_history[index].hist_fh, 0, SEEK_END);
13148    irec.offset = TELL(_history[index].hist_fh);
13149 
13150    /* write record header */
13151    write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
13152 
13153    /* write data */
13154    write(_history[index].hist_fh, (char *) data, size);
13155 
13156    /* write index record */
13157    lseek(_history[index].index_fh, 0, SEEK_END);
13158    if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) < sizeof(irec))
13159       return HS_FILE_ERROR;
13160 
13161    return HS_SUCCESS;
13162 }
13163 
13164 
13165 /********************************************************************/
13166 INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size,
13167                    INT event_id[], DWORD * id_size)
13168 /********************************************************************\
13169 
13170   Routine: hs_enum_events
13171 
13172   Purpose: Enumerate events for a given date
13173 
13174   Input:
13175     DWORD  ltime            Date at which events should be enumerated
13176 
13177   Output:
13178     char   *event_name      Array containing event names
13179     DWORD  *name_size       Size of name array
13180     char   *event_id        Array containing event IDs
13181     DWORD  *id_size         Size of ID array
13182 
13183   Function value:
13184     HS_SUCCESS              Successful completion
13185     HS_NO_MEMEORY           Out of memory
13186     HS_FILE_ERROR           Cannot open history file
13187 
13188 \********************************************************************/
13189 {
13190    int fh, fhd;
13191    INT status, i, j, n;
13192    DEF_RECORD def_rec;
13193 
13194    if (rpc_is_remote())
13195       return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name, name_size, event_id,
13196                       id_size);
13197 
13198    /* search latest history file */
13199    status = hs_search_file(&ltime, -1);
13200    if (status != HS_SUCCESS) {
13201       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
13202       return HS_FILE_ERROR;
13203    }
13204 
13205    /* open history and definition files */
13206    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13207    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13208    if (fh < 0 || fhd < 0) {
13209       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
13210       return HS_FILE_ERROR;
13211    }
13212    lseek(fhd, 0, SEEK_SET);
13213 
13214    /* loop over definition index file */
13215    n = 0;
13216    do {
13217       /* read event definition */
13218       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13219       if (j < (int) sizeof(def_rec))
13220          break;
13221 
13222       /* look for existing entry for this event id */
13223       for (i = 0; i < n; i++)
13224          if (event_id[i] == (INT) def_rec.event_id) {
13225             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13226             break;
13227          }
13228 
13229       /* new entry found */
13230       if (i == n) {
13231          if (i * NAME_LENGTH > (INT) * name_size || i * sizeof(INT) > (INT) * id_size) {
13232             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
13233             close(fh);
13234             close(fhd);
13235             return HS_NO_MEMORY;
13236          }
13237 
13238          /* copy definition record */
13239          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13240          event_id[i] = def_rec.event_id;
13241          n++;
13242       }
13243    } while (TRUE);
13244 
13245    close(fh);
13246    close(fhd);
13247    *name_size = n * NAME_LENGTH;
13248    *id_size = n * sizeof(INT);
13249 
13250    return HS_SUCCESS;
13251 }
13252 
13253 
13254 /********************************************************************/
13255 INT hs_count_events(DWORD ltime, DWORD * count)
13256 /********************************************************************\
13257 
13258   Routine: hs_count_events
13259 
13260   Purpose: Count number of different events for a given date
13261 
13262   Input:
13263     DWORD  ltime            Date at which events should be counted
13264 
13265   Output:
13266     DWORD  *count           Number of different events found
13267 
13268   Function value:
13269     HS_SUCCESS              Successful completion
13270     HS_FILE_ERROR           Cannot open history file
13271 
13272 \********************************************************************/
13273 {
13274    int fh, fhd;
13275    INT status, i, j, n;
13276    DWORD *id;
13277    DEF_RECORD def_rec;
13278 
13279    if (rpc_is_remote())
13280       return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
13281 
13282    /* search latest history file */
13283    status = hs_search_file(&ltime, -1);
13284    if (status != HS_SUCCESS) {
13285       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13286       return HS_FILE_ERROR;
13287    }
13288 
13289    /* open history and definition files */
13290    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13291    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13292    if (fh < 0 || fhd < 0) {
13293       cm_msg(MERROR, "hs_count_events", "cannot open index files");
13294       return HS_FILE_ERROR;
13295    }
13296 
13297    /* allocate event id array */
13298    lseek(fhd, 0, SEEK_END);
13299    id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
13300    lseek(fhd, 0, SEEK_SET);
13301 
13302    /* loop over index file */
13303    n = 0;
13304    do {
13305       /* read definition index record */
13306       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13307       if (j < (int) sizeof(def_rec))
13308          break;
13309 
13310       /* look for existing entries */
13311       for (i = 0; i < n; i++)
13312          if (id[i] == def_rec.event_id)
13313             break;
13314 
13315       /* new entry found */
13316       if (i == n) {
13317          id[i] = def_rec.event_id;
13318          n++;
13319       }
13320    } while (TRUE);
13321 
13322 
13323    M_FREE(id);
13324    close(fh);
13325    close(fhd);
13326    *count = n;
13327 
13328    return HS_SUCCESS;
13329 }
13330 
13331 
13332 /********************************************************************/
13333 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
13334 /********************************************************************\
13335 
13336   Routine: hs_get_event_id
13337 
13338   Purpose: Return event ID for a given name. If event cannot be found
13339            in current definition file, go back in time until found
13340 
13341   Input:
13342     DWORD  ltime            Date at which event ID should be looked for
13343 
13344   Output:
13345     DWORD  *id              Event ID
13346 
13347   Function value:
13348     HS_SUCCESS              Successful completion
13349     HS_FILE_ERROR           Cannot open history file
13350     HS_UNDEFINED_EVENT      Event "name" not found
13351 
13352 \********************************************************************/
13353 {
13354    int fh, fhd;
13355    INT status, i;
13356    DWORD lt;
13357    DEF_RECORD def_rec;
13358 
13359    if (rpc_is_remote())
13360       return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
13361 
13362    /* search latest history file */
13363    if (ltime == 0)
13364       ltime = time(NULL);
13365 
13366    lt = ltime;
13367 
13368    do {
13369       status = hs_search_file(&lt, -1);
13370       if (status != HS_SUCCESS) {
13371          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13372          return HS_FILE_ERROR;
13373       }
13374 
13375       /* open history and definition files */
13376       hs_open_file(lt, "hst", O_RDONLY, &fh);
13377       hs_open_file(lt, "idf", O_RDONLY, &fhd);
13378       if (fh < 0 || fhd < 0) {
13379          cm_msg(MERROR, "hs_count_events", "cannot open index files");
13380          return HS_FILE_ERROR;
13381       }
13382 
13383       /* loop over index file */
13384       *id = 0;
13385       do {
13386          /* read definition index record */
13387          i = read(fhd, (char *) &def_rec, sizeof(def_rec));
13388          if (i < (int) sizeof(def_rec))
13389             break;
13390 
13391          if (strcmp(name, def_rec.event_name) == 0) {
13392             *id = def_rec.event_id;
13393             close(fh);
13394             close(fhd);
13395             return HS_SUCCESS;
13396          }
13397       } while (TRUE);
13398 
13399       close(fh);
13400       close(fhd);
13401 
13402       /* not found -> go back one day */
13403       lt -= 3600 * 24;
13404 
13405    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
13406 
13407    return HS_UNDEFINED_EVENT;
13408 }
13409 
13410 
13411 /********************************************************************/
13412 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
13413 /********************************************************************\
13414 
13415   Routine: hs_count_vars
13416 
13417   Purpose: Count number of variables for a given date and event id
13418 
13419   Input:
13420     DWORD  ltime            Date at which tags should be counted
13421 
13422   Output:
13423     DWORD  *count           Number of tags
13424 
13425   Function value:
13426     HS_SUCCESS              Successful completion
13427     HS_FILE_ERROR           Cannot open history file
13428 
13429 \********************************************************************/
13430 {
13431    int fh, fhd;
13432    INT i, n, status;
13433    DEF_RECORD def_rec;
13434    HIST_RECORD rec;
13435 
13436    if (rpc_is_remote())
13437       return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
13438 
13439    /* search latest history file */
13440    status = hs_search_file(&ltime, -1);
13441    if (status != HS_SUCCESS) {
13442       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
13443       return HS_FILE_ERROR;
13444    }
13445 
13446    /* open history and definition files */
13447    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13448    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13449    if (fh < 0 || fhd < 0) {
13450       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
13451       return HS_FILE_ERROR;
13452    }
13453 
13454    /* search last definition */
13455    lseek(fhd, 0, SEEK_END);
13456    n = TELL(fhd) / sizeof(def_rec);
13457    def_rec.event_id = 0;
13458    for (i = n - 1; i >= 0; i--) {
13459       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13460       read(fhd, (char *) &def_rec, sizeof(def_rec));
13461       if (def_rec.event_id == event_id)
13462          break;
13463    }
13464    if (def_rec.event_id != event_id) {
13465       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
13466       return HS_FILE_ERROR;
13467    }
13468 
13469    /* read definition */
13470    lseek(fh, def_rec.def_offset, SEEK_SET);
13471    read(fh, (char *) &rec, sizeof(rec));
13472    *count = rec.data_size / sizeof(TAG);
13473 
13474    close(fh);
13475    close(fhd);
13476 
13477    return HS_SUCCESS;
13478 }
13479 
13480 
13481 /********************************************************************/
13482 INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD * size,
13483                  DWORD * var_n, DWORD * n_size)
13484 /********************************************************************\
13485 
13486   Routine: hs_enum_vars
13487 
13488   Purpose: Enumerate variable tags for a given date and event id
13489 
13490   Input:
13491     DWORD  ltime            Date at which tags should be enumerated
13492     DWORD  event_id         Event ID
13493 
13494   Output:
13495     char   *var_name        Array containing variable names
13496     DWORD  *size            Size of name array
13497     DWORD  *var_n           Array size of variable
13498     DWORD  *n_size          Size of n array
13499 
13500   Function value:
13501     HS_SUCCESS              Successful completion
13502     HS_NO_MEMEORY           Out of memory
13503     HS_FILE_ERROR           Cannot open history file
13504 
13505 \********************************************************************/
13506 {
13507    char str[256];
13508    int fh, fhd;
13509    INT i, n, status;
13510    DEF_RECORD def_rec;
13511    HIST_RECORD rec;
13512    TAG *tag;
13513 
13514    if (rpc_is_remote())
13515       return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
13516 
13517    /* search latest history file */
13518    status = hs_search_file(&ltime, -1);
13519    if (status != HS_SUCCESS) {
13520       cm_msg(MERROR, "hs_enum_vars", "cannot find recent history file");
13521       return HS_FILE_ERROR;
13522    }
13523 
13524    /* open history and definition files */
13525    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13526    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13527    if (fh < 0 || fhd < 0) {
13528       cm_msg(MERROR, "hs_enum_vars", "cannot open index files");
13529       return HS_FILE_ERROR;
13530    }
13531 
13532    /* search last definition */
13533    lseek(fhd, 0, SEEK_END);
13534    n = TELL(fhd) / sizeof(def_rec);
13535    def_rec.event_id = 0;
13536    for (i = n - 1; i >= 0; i--) {
13537       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13538       read(fhd, (char *) &def_rec, sizeof(def_rec));
13539       if (def_rec.event_id == event_id)
13540          break;
13541    }
13542    if (def_rec.event_id != event_id) {
13543       cm_msg(MERROR, "hs_enum_vars", "event %d not found in index file", event_id);
13544       return HS_FILE_ERROR;
13545    }
13546 
13547    /* read definition header */
13548    lseek(fh, def_rec.def_offset, SEEK_SET);
13549    read(fh, (char *) &rec, sizeof(rec));
13550    read(fh, str, NAME_LENGTH);
13551 
13552    /* read event definition */
13553    n = rec.data_size / sizeof(TAG);
13554    tag = (TAG *) M_MALLOC(rec.data_size);
13555    read(fh, (char *) tag, rec.data_size);
13556 
13557    if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
13558 
13559       /* store partial definition */
13560       for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
13561          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13562          var_n[i] = tag[i].n_data;
13563       }
13564 
13565       cm_msg(MERROR, "hs_enum_vars", "tag buffer too small");
13566       M_FREE(tag);
13567       close(fh);
13568       close(fhd);
13569       return HS_NO_MEMORY;
13570    }
13571 
13572    /* store full definition */
13573    for (i = 0; i < n; i++) {
13574       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13575       var_n[i] = tag[i].n_data;
13576    }
13577    *size = n * NAME_LENGTH;
13578    *n_size = n * sizeof(DWORD);
13579 
13580    M_FREE(tag);
13581    close(fh);
13582    close(fhd);
13583 
13584    return HS_SUCCESS;
13585 }
13586 
13587 
13588 /********************************************************************/
13589 INT hs_get_var(DWORD ltime, DWORD event_id, char *var_name, DWORD * type, INT * n_data)
13590 /********************************************************************\
13591 
13592   Routine: hs_get_var
13593 
13594   Purpose: Get definition for certain variable
13595 
13596   Input:
13597     DWORD  ltime            Date at which variable definition should
13598                             be returned
13599     DWORD  event_id         Event ID
13600     char   *var_name        Name of variable
13601 
13602   Output:
13603     INT    *type            Type of variable
13604     INT    *n_data          Number of items in variable
13605 
13606   Function value:
13607     HS_SUCCESS              Successful completion
13608     HS_NO_MEMEORY           Out of memory
13609     HS_FILE_ERROR           Cannot open history file
13610 
13611 \********************************************************************/
13612 {
13613    char str[256];
13614    int fh, fhd;
13615    INT i, n, status;
13616    DEF_RECORD def_rec;
13617    HIST_RECORD rec;
13618    TAG *tag;
13619 
13620    if (rpc_is_remote())
13621       return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name, type, n_data);
13622 
13623    /* search latest history file */
13624    status = hs_search_file(&ltime, -1);
13625    if (status != HS_SUCCESS) {
13626       cm_msg(MERROR, "hs_get_var", "cannot find recent history file");
13627       return HS_FILE_ERROR;
13628    }
13629 
13630    /* open history and definition files */
13631    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13632    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13633    if (fh < 0 || fhd < 0) {
13634       cm_msg(MERROR, "hs_get_var", "cannot open index files");
13635       return HS_FILE_ERROR;
13636    }
13637 
13638    /* search last definition */
13639    lseek(fhd, 0, SEEK_END);
13640    n = TELL(fhd) / sizeof(def_rec);
13641    def_rec.event_id = 0;
13642    for (i = n - 1; i >= 0; i--) {
13643       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13644       read(fhd, (char *) &def_rec, sizeof(def_rec));
13645       if (def_rec.event_id == event_id)
13646          break;
13647    }
13648    if (def_rec.event_id != event_id) {
13649       cm_msg(MERROR, "hs_get_var", "event %d not found in index file", event_id);
13650       return HS_FILE_ERROR;
13651    }
13652 
13653    /* read definition header */
13654    lseek(fh, def_rec.def_offset, SEEK_SET);
13655    read(fh, (char *) &rec, sizeof(rec));
13656    read(fh, str, NAME_LENGTH);
13657 
13658    /* read event definition */
13659    n = rec.data_size / sizeof(TAG);
13660    tag = (TAG *) M_MALLOC(rec.data_size);
13661    read(fh, (char *) tag, rec.data_size);
13662 
13663    /* search variable */
13664    for (i = 0; i < n; i++)
13665       if (strcmp(tag[i].name, var_name) == 0)
13666          break;
13667 
13668    close(fh);
13669    close(fhd);
13670 
13671    if (i < n) {
13672       *type = tag[i].type;
13673       *n_data = tag[i].n_data;
13674    } else {
13675       *type = *n_data = 0;
13676       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
13677       M_FREE(tag);
13678       return HS_UNDEFINED_VAR;
13679    }
13680 
13681    M_FREE(tag);
13682    return HS_SUCCESS;
13683 }
13684 
13685 
13686 /********************************************************************/
13687 INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time,
13688             DWORD interval, char *tag_name, DWORD var_index,
13689             DWORD * time_buffer, DWORD * tbsize,
13690             void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
13691 /********************************************************************\
13692 
13693   Routine: hs_read
13694 
13695   Purpose: Read history for a variable at a certain time interval
13696 
13697   Input:
13698     DWORD  event_id         Event ID
13699     DWORD  start_time       Starting Date/Time
13700     DWORD  end_time         End Date/Time
13701     DWORD  interval         Minimum time in seconds between reported
13702                             events. Can be used to skip events
13703     char   *tag_name        Variable name inside event
13704     DWORD  var_index        Index if variable is array
13705 
13706   Output:
13707     DWORD  *time_buffer     Buffer containing times for each value
13708     DWORD  *tbsize          Size of time buffer
13709     void   *data_buffer     Buffer containing variable values
13710     DWORD  *dbsize          Data buffer size
13711     DWORD  *type            Type of variable (one of TID_xxx)
13712     DWORD  *n               Number of time/value pairs found
13713                             in specified interval and placed into
13714                             time_buffer and data_buffer
13715 
13716 
13717   Function value:
13718     HS_SUCCESS              Successful completion
13719     HS_NO_MEMEORY           Out of memory
13720     HS_FILE_ERROR           Cannot open history file
13721     HS_WRONG_INDEX          var_index exceeds array size of variable
13722     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
13723     HS_TRUNCATED            Buffer too small, data has been truncated
13724 
13725 \********************************************************************/
13726 {
13727    DWORD prev_time, last_irec_time;
13728    int fh, fhd, fhi, cp = 0;
13729    INT i, delta, index = 0, status, cache_size;
13730    INDEX_RECORD irec, *pirec;
13731    HIST_RECORD rec, drec;
13732    INT old_def_offset, var_size = 0, var_offset = 0;
13733    TAG *tag;
13734    char str[NAME_LENGTH];
13735    struct tm *tms;
13736    char *cache;
13737 
13738    if (rpc_is_remote())
13739       return rpc_call(RPC_HS_READ, event_id, start_time, end_time, interval,
13740                       tag_name, var_index, time_buffer, tbsize, data_buffer,
13741                       dbsize, type, n);
13742 
13743    /* if not time given, use present to one hour in past */
13744    if (start_time == 0)
13745       start_time = time(NULL) - 3600;
13746    if (end_time == 0)
13747       end_time = time(NULL);
13748 
13749    /* search history file for start_time */
13750    status = hs_search_file(&start_time, 1);
13751    if (status != HS_SUCCESS) {
13752       cm_msg(MERROR, "hs_read", "cannot find recent history file");
13753       *tbsize = *dbsize = *n = 0;
13754       return HS_FILE_ERROR;
13755    }
13756 
13757    /* open history and definition files */
13758    hs_open_file(start_time, "hst", O_RDONLY, &fh);
13759    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
13760    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
13761    if (fh < 0 || fhd < 0 || fhi < 0) {
13762       cm_msg(MERROR, "hs_read", "cannot open index files");
13763       *tbsize = *dbsize = *n = 0;
13764       return HS_FILE_ERROR;
13765    }
13766 
13767    /* try to read index file into cache */
13768    lseek(fhi, 0, SEEK_END);
13769    cache_size = TELL(fhi);
13770 
13771    if (cache_size > 0) {
13772       cache = (char *) M_MALLOC(cache_size);
13773       if (cache) {
13774          lseek(fhi, 0, SEEK_SET);
13775          i = read(fhi, cache, cache_size);
13776          if (i < cache_size) {
13777             M_FREE(cache);
13778             close(fh);
13779             close(fhd);
13780             close(fhi);
13781             return HS_FILE_ERROR;
13782          }
13783       }
13784 
13785       /* search record closest to start time */
13786       if (cache == NULL) {
13787          lseek(fhi, 0, SEEK_END);
13788          delta = (TELL(fhi) / sizeof(irec)) / 2;
13789          lseek(fhi, delta * sizeof(irec), SEEK_SET);
13790          do {
13791             delta = (int) (abs(delta) / 2.0 + 0.5);
13792             read(fhi, (char *) &irec, sizeof(irec));
13793             if (irec.time > start_time)
13794                delta = -delta;
13795 
13796             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
13797          } while (abs(delta) > 1 && irec.time != start_time);
13798          read(fhi, (char *) &irec, sizeof(irec));
13799          if (irec.time > start_time)
13800             delta = -abs(delta);
13801 
13802          i = TELL(fhi) + (delta - 1) * sizeof(irec);
13803          if (i <= 0)
13804             lseek(fhi, 0, SEEK_SET);
13805          else
13806             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
13807          read(fhi, (char *) &irec, sizeof(irec));
13808       } else {
13809          delta = (cache_size / sizeof(irec)) / 2;
13810          cp = delta * sizeof(irec);
13811          do {
13812             delta = (int) (abs(delta) / 2.0 + 0.5);
13813             pirec = (INDEX_RECORD *) (cache + cp);
13814             if (pirec->time > start_time)
13815                delta = -delta;
13816 
13817             cp = cp + delta * sizeof(irec);
13818          } while (abs(delta) > 1 && pirec->time != start_time);
13819          pirec = (INDEX_RECORD *) (cache + cp);
13820          if (pirec->time > start_time)
13821             delta = -abs(delta);
13822 
13823          if (cp <= delta * (int) sizeof(irec))
13824             cp = 0;
13825          else
13826             cp = cp + delta * sizeof(irec);
13827 
13828          if (cp >= cache_size)
13829             cp = cache_size - sizeof(irec);
13830          if (cp < 0)
13831             cp = 0;
13832 
13833          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
13834          cp += sizeof(irec);
13835       }
13836    } else {                     /* file size > 0 */
13837 
13838       cache = NULL;
13839       irec.time = start_time;
13840    }
13841 
13842    /* read records, skip wrong IDs */
13843    old_def_offset = -1;
13844    *n = 0;
13845    prev_time = 0;
13846    last_irec_time = 0;
13847    do {
13848       if (irec.time < last_irec_time) {
13849          cm_msg(MERROR, "hs_read",
13850                 "corrupted history data: time does not increase: %d -> %d",
13851                 last_irec_time, irec.time);
13852          *tbsize = *dbsize = *n = 0;
13853          return HS_FILE_ERROR;
13854       }
13855       last_irec_time = irec.time;
13856       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
13857          /* check if record time more than "interval" seconds after previous time */
13858          if (irec.time >= prev_time + interval) {
13859             prev_time = irec.time;
13860             lseek(fh, irec.offset, SEEK_SET);
13861             read(fh, (char *) &rec, sizeof(rec));
13862 
13863             /* if definition changed, read new definition */
13864             if ((INT) rec.def_offset != old_def_offset) {
13865                lseek(fh, rec.def_offset, SEEK_SET);
13866                read(fh, (char *) &drec, sizeof(drec));
13867                read(fh, str, NAME_LENGTH);
13868 
13869                tag = (TAG *) M_MALLOC(drec.data_size);
13870                if (tag == NULL) {
13871                   *n = *tbsize = *dbsize = 0;
13872                   if (cache)
13873                      M_FREE(cache);
13874                   close(fh);
13875                   close(fhd);
13876                   close(fhi);
13877                   return HS_NO_MEMORY;
13878                }
13879                read(fh, (char *) tag, drec.data_size);
13880 
13881                /* find index of tag_name in new definition */
13882                index = -1;
13883                for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
13884                   if (equal_ustring(tag[i].name, tag_name)) {
13885                      index = i;
13886                      break;
13887                   }
13888 
13889                /*
13890                   if ((DWORD) i == drec.data_size/sizeof(TAG))
13891                   {
13892                   *n = *tbsize = *dbsize = 0;
13893                   if (cache)
13894                   M_FREE(cache);
13895 
13896                   return HS_UNDEFINED_VAR;
13897                   }
13898                 */
13899 
13900                if (index >= 0 && var_index >= tag[i].n_data) {
13901                   *n = *tbsize = *dbsize = 0;
13902                   if (cache)
13903                      M_FREE(cache);
13904                   M_FREE(tag);
13905                   close(fh);
13906                   close(fhd);
13907                   close(fhi);
13908                   return HS_WRONG_INDEX;
13909                }
13910 
13911                /* calculate offset for variable */
13912                if (index >= 0) {
13913                   *type = tag[i].type;
13914 
13915                   /* loop over all previous variables */
13916                   for (i = 0, var_offset = 0; i < index; i++)
13917                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
13918 
13919                   /* strings have size n_data */
13920                   if (tag[index].type == TID_STRING)
13921                      var_size = tag[i].n_data;
13922                   else
13923                      var_size = rpc_tid_size(tag[index].type);
13924 
13925                   var_offset += var_size * var_index;
13926                }
13927 
13928                M_FREE(tag);
13929                old_def_offset = rec.def_offset;
13930                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
13931             }
13932 
13933             if (index >= 0) {
13934                /* check if data fits in buffers */
13935                if ((*n) * sizeof(DWORD) >= *tbsize || (*n) * var_size >= *dbsize) {
13936                   *dbsize = (*n) * var_size;
13937                   *tbsize = (*n) * sizeof(DWORD);
13938                   if (cache)
13939                      M_FREE(cache);
13940                   close(fh);
13941                   close(fhd);
13942                   close(fhi);
13943                   return HS_TRUNCATED;
13944                }
13945 
13946                /* copy time from header */
13947                time_buffer[*n] = irec.time;
13948 
13949                /* copy data from record */
13950                lseek(fh, var_offset, SEEK_CUR);
13951                read(fh, (char *) data_buffer + (*n) * var_size, var_size);
13952 
13953                /* increment counter */
13954                (*n)++;
13955             }
13956          }
13957       }
13958 
13959       /* read next index record */
13960       if (cache) {
13961          if (cp >= cache_size) {
13962             i = -1;
13963             M_FREE(cache);
13964             cache = NULL;
13965          } else
13966             i = sizeof(irec);
13967 
13968          if (cp < cache_size) {
13969             memcpy(&irec, cache + cp, sizeof(irec));
13970             cp += sizeof(irec);
13971          }
13972       } else
13973          i = read(fhi, (char *) &irec, sizeof(irec));
13974 
13975       /* end of file: search next history file */
13976       if (i <= 0) {
13977          close(fh);
13978          close(fhd);
13979          close(fhi);
13980 
13981          /* advance one day */
13982          tms = localtime((const time_t *) &last_irec_time);
13983          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
13984          last_irec_time = mktime(tms);
13985 
13986          last_irec_time += 3600 * 24;
13987 
13988          if (last_irec_time > end_time)
13989             break;
13990 
13991          /* search next file */
13992          status = hs_search_file(&last_irec_time, 1);
13993          if (status != HS_SUCCESS)
13994             break;
13995 
13996          /* open history and definition files */
13997          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
13998          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
13999          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14000          if (fh < 0 || fhd < 0 || fhi < 0) {
14001             cm_msg(MERROR, "hs_read", "cannot open index files");
14002             break;
14003          }
14004 
14005          /* try to read index file into cache */
14006          lseek(fhi, 0, SEEK_END);
14007          cache_size = TELL(fhi);
14008          lseek(fhi, 0, SEEK_SET);
14009          cache = (char *) M_MALLOC(cache_size);
14010          if (cache) {
14011             i = read(fhi, cache, cache_size);
14012             if (i < cache_size)
14013                break;
14014             /* read first record */
14015             cp = 0;
14016             memcpy(&irec, cache, sizeof(irec));
14017          } else {
14018             /* read first record */
14019             i = read(fhi, (char *) &irec, sizeof(irec));
14020             if (i <= 0)
14021                break;
14022          }
14023 
14024          /* old definition becomes invalid */
14025          old_def_offset = -1;
14026       }
14027    } while (irec.time < end_time);
14028 
14029    if (cache)
14030       M_FREE(cache);
14031    close(fh);
14032    close(fhd);
14033    close(fhi);
14034 
14035    *dbsize = *n * var_size;
14036    *tbsize = *n * sizeof(DWORD);
14037 
14038    return HS_SUCCESS;
14039 }
14040 
14041 
14042 /********************************************************************/
14043 INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time,
14044             DWORD interval, BOOL binary_time)
14045 /********************************************************************\
14046 
14047   Routine: hs_dump
14048 
14049   Purpose: Display history for a given event at stdout. The output
14050            can be redirected to be read by Excel for example.
14051 
14052   Input:
14053     DWORD  event_id         Event ID
14054     DWORD  start_time       Starting Date/Time
14055     DWORD  end_time         End Date/Time
14056     DWORD  interval         Minimum time in seconds between reported
14057                             events. Can be used to skip events
14058     BOOL   binary_time      Display DWORD time stamp
14059   Output:
14060     <screen output>
14061 
14062   Function value:
14063     HS_SUCCESS              Successful completion
14064     HS_FILE_ERROR           Cannot open history file
14065 
14066 \********************************************************************/
14067 {
14068    DWORD prev_time, last_irec_time;
14069    int fh, fhd, fhi;
14070    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
14071    INDEX_RECORD irec;
14072    HIST_RECORD rec, drec;
14073    INT old_def_offset, offset;
14074    TAG *tag = NULL, *old_tag = NULL;
14075    char str[NAME_LENGTH], data_buffer[10000];
14076    struct tm *tms;
14077 
14078    /* if not time given, use present to one hour in past */
14079    if (start_time == 0)
14080       start_time = time(NULL) - 3600;
14081    if (end_time == 0)
14082       end_time = time(NULL);
14083 
14084    /* search history file for start_time */
14085    status = hs_search_file(&start_time, 1);
14086    if (status != HS_SUCCESS) {
14087       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
14088       return HS_FILE_ERROR;
14089    }
14090 
14091    /* open history and definition files */
14092    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14093    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14094    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14095    if (fh < 0 || fhd < 0 || fhi < 0) {
14096       cm_msg(MERROR, "hs_dump", "cannot open index files");
14097       return HS_FILE_ERROR;
14098    }
14099 
14100    /* search record closest to start time */
14101    lseek(fhi, 0, SEEK_END);
14102    delta = (TELL(fhi) / sizeof(irec)) / 2;
14103    lseek(fhi, delta * sizeof(irec), SEEK_SET);
14104    do {
14105       delta = (int) (abs(delta) / 2.0 + 0.5);
14106       read(fhi, (char *) &irec, sizeof(irec));
14107       if (irec.time > start_time)
14108          delta = -delta;
14109 
14110       i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14111    } while (abs(delta) > 1 && irec.time != start_time);
14112    read(fhi, (char *) &irec, sizeof(irec));
14113    if (irec.time > start_time)
14114       delta = -abs(delta);
14115 
14116    i = TELL(fhi) + (delta - 1) * sizeof(irec);
14117    if (i <= 0)
14118       lseek(fhi, 0, SEEK_SET);
14119    else
14120       lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14121    read(fhi, (char *) &irec, sizeof(irec));
14122 
14123    /* read records, skip wrong IDs */
14124    old_def_offset = -1;
14125    prev_time = 0;
14126    last_irec_time = 0;
14127    do {
14128       if (irec.time < last_irec_time) {
14129          cm_msg(MERROR, "hs_dump",
14130                 "corrupted history data: time does not increase: %d -> %d",
14131                 last_irec_time, irec.time);
14132          return HS_FILE_ERROR;
14133       }
14134       last_irec_time = irec.time;
14135       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14136          if (irec.time >= prev_time + interval) {
14137             prev_time = irec.time;
14138             lseek(fh, irec.offset, SEEK_SET);
14139             read(fh, (char *) &rec, sizeof(rec));
14140 
14141             /* if definition changed, read new definition */
14142             if ((INT) rec.def_offset != old_def_offset) {
14143                lseek(fh, rec.def_offset, SEEK_SET);
14144                read(fh, (char *) &drec, sizeof(drec));
14145                read(fh, str, NAME_LENGTH);
14146 
14147                if (tag == NULL)
14148                   tag = (TAG *) M_MALLOC(drec.data_size);
14149                else
14150                   tag = (TAG *) realloc(tag, drec.data_size);
14151                if (tag == NULL)
14152                   return HS_NO_MEMORY;
14153                read(fh, (char *) tag, drec.data_size);
14154                n_tag = drec.data_size / sizeof(TAG);
14155 
14156                /* print tag names if definition has changed */
14157                if (old_tag == NULL || old_n_tag != n_tag ||
14158                    memcmp(old_tag, tag, drec.data_size) != 0) {
14159                   printf("Date\t");
14160                   for (i = 0; i < n_tag; i++) {
14161                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
14162                         printf("%s\t", tag[i].name);
14163                      else
14164                         for (j = 0; j < (INT) tag[i].n_data; j++)
14165                            printf("%s%d\t", tag[i].name, j);
14166                   }
14167                   printf("\n");
14168 
14169                   if (old_tag == NULL)
14170                      old_tag = (TAG *) M_MALLOC(drec.data_size);
14171                   else
14172                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
14173                   memcpy(old_tag, tag, drec.data_size);
14174                   old_n_tag = n_tag;
14175                }
14176 
14177                old_def_offset = rec.def_offset;
14178                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14179             }
14180 
14181             /* print time from header */
14182             if (binary_time)
14183                printf("%li ", irec.time);
14184             else {
14185                sprintf(str, "%s", ctime((const time_t *) &irec.time) + 4);
14186                str[20] = '\t';
14187                printf(str);
14188             }
14189 
14190             /* read data */
14191             read(fh, data_buffer, rec.data_size);
14192 
14193             /* interprete data from tag definition */
14194             offset = 0;
14195             for (i = 0; i < n_tag; i++) {
14196                /* strings have a length of n_data */
14197                if (tag[i].type == TID_STRING) {
14198                   printf("%s\t", data_buffer + offset);
14199                   offset += tag[i].n_data;
14200                } else if (tag[i].n_data == 1) {
14201                   /* non-array data */
14202                   db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14203                              tag[i].type);
14204                   printf("%s\t", str);
14205                   offset += rpc_tid_size(tag[i].type);
14206                } else
14207                   /* loop over array data */
14208                   for (j = 0; j < (INT) tag[i].n_data; j++) {
14209                      db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14210                                 tag[i].type);
14211                      printf("%s\t", str);
14212                      offset += rpc_tid_size(tag[i].type);
14213                   }
14214             }
14215             printf("\n");
14216          }
14217       }
14218 
14219       /* read next index record */
14220       i = read(fhi, (char *) &irec, sizeof(irec));
14221 
14222       /* end of file: search next history file */
14223       if (i <= 0) {
14224          close(fh);
14225          close(fhd);
14226          close(fhi);
14227 
14228          /* advance one day */
14229          tms = localtime((const time_t *) &last_irec_time);
14230          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14231          last_irec_time = mktime(tms);
14232 
14233          last_irec_time += 3600 * 24;
14234          if (last_irec_time > end_time)
14235             break;
14236 
14237          /* search next file */
14238          status = hs_search_file(&last_irec_time, 1);
14239          if (status != HS_SUCCESS)
14240             break;
14241 
14242          /* open history and definition files */
14243          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14244          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14245          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14246          if (fh < 0 || fhd < 0 || fhi < 0) {
14247             cm_msg(MERROR, "hs_dump", "cannot open index files");
14248             break;
14249          }
14250 
14251          /* read first record */
14252          i = read(fhi, (char *) &irec, sizeof(irec));
14253          if (i <= 0)
14254             break;
14255 
14256          /* old definition becomes invalid */
14257          old_def_offset = -1;
14258       }
14259    } while (irec.time < end_time);
14260 
14261    M_FREE(tag);
14262    M_FREE(old_tag);
14263    close(fh);
14264    close(fhd);
14265    close(fhi);
14266 
14267    return HS_SUCCESS;
14268 }
14269 
14270 
14271 /********************************************************************/
14272 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
14273 /********************************************************************\
14274 
14275   Routine: hs_fdump
14276 
14277   Purpose: Display history for a given history file
14278 
14279   Input:
14280     char   *file_name       Name of file to dump
14281     DWORD  event_id         Event ID
14282     BOOL   binary_time      Display DWORD time stamp
14283 
14284   Output:
14285     <screen output>
14286 
14287   Function value:
14288     HS_SUCCESS              Successful completion
14289     HS_FILE_ERROR           Cannot open history file
14290 
14291 \********************************************************************/
14292 {
14293    int fh;
14294    INT n;
14295    HIST_RECORD rec;
14296    char event_name[NAME_LENGTH];
14297    char str[80];
14298 
14299    /* open file, add O_BINARY flag for Windows NT */
14300    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
14301    if (fh < 0) {
14302       cm_msg(MERROR, "hs_fdump", "cannot open file %s", file_name);
14303       return HS_FILE_ERROR;
14304    }
14305 
14306    /* loop over file records in .hst file */
14307    do {
14308       n = read(fh, (char *) &rec, sizeof(rec));
14309       if (n < sizeof(rec))
14310          break;
14311 
14312       /* check if record type is definition */
14313       if (rec.record_type == RT_DEF) {
14314          /* read name */
14315          read(fh, event_name, sizeof(event_name));
14316 
14317          if (rec.event_id == id || id == 0)
14318             printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
14319 
14320          /* skip tags */
14321          lseek(fh, rec.data_size, SEEK_CUR);
14322       } else {
14323          /* print data record */
14324          if (binary_time)
14325             sprintf(str, "%li ", rec.time);
14326          else {
14327             strcpy(str, ctime((const time_t *) &rec.time) + 4);
14328             str[15] = 0;
14329          }
14330          if (rec.event_id == id || id == 0)
14331             printf("ID %ld, %s, size %ld\n", rec.event_id, str, rec.data_size);
14332 
14333          /* skip data */
14334          lseek(fh, rec.data_size, SEEK_CUR);
14335       }
14336 
14337    } while (TRUE);
14338 
14339    close(fh);
14340 
14341    return HS_SUCCESS;
14342 }
14343 #endif                          /* OS_VXWORKS hs section */
14344 
14345 /**dox***************************************************************/
14346 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14347 
14348 /**dox***************************************************************/
14349                    /** @} *//* end of hsfunctionc */
14350 
14351 /**dox***************************************************************/
14352 /** @addtogroup elfunctionc
14353  *  
14354  *  @{  */
14355 
14356 /**dox***************************************************************/
14357 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14358 
14359 /********************************************************************\
14360 *                                                                    *
14361 *               Electronic logbook functions                         *
14362 *                                                                    *
14363 \********************************************************************/
14364 
14365 /********************************************************************/
14366 void el_decode(char *message, char *key, char *result, int size)
14367 {
14368    char *rstart = result;
14369    char *pc;
14370 
14371    if (result == NULL)
14372       return;
14373 
14374    *result = 0;
14375 
14376    if (strstr(message, key)) {
14377       for (pc = strstr(message, key) + strlen(key); *pc != '\n';)
14378          *result++ = *pc++;
14379       *result = 0;
14380    }
14381 
14382    assert((int) strlen(rstart) < size);
14383 }
14384 
14385 /**dox***************************************************************/
14386 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14387 
14388 /********************************************************************/
14389 /**
14390 Submit an ELog entry.
14391 @param run  Run Number.
14392 @param author Message author.
14393 @param type Message type.
14394 @param system Message system.
14395 @param subject Subject.
14396 @param text Message text.
14397 @param reply_to In reply to this message.
14398 @param encoding Text encoding, either HTML or plain.
14399 @param afilename1   File name of attachment.
14400 @param buffer1      File contents.
14401 @param buffer_size1 Size of buffer in bytes.
14402 @param afilename2   File name of attachment.
14403 @param buffer2      File contents.
14404 @param buffer_size2 Size of buffer in bytes.
14405 @param afilename3   File name of attachment.
14406 @param buffer3      File contents.
14407 @param buffer_size3 Size of buffer in bytes.
14408 @param tag          If given, edit existing message.
14409 @param tag_size     Maximum size of tag.
14410 @return EL_SUCCESS
14411 */
14412 INT el_submit(int run, char *author, char *type, char *system, char *subject,
14413               char *text, char *reply_to, char *encoding,
14414               char *afilename1, char *buffer1, INT buffer_size1,
14415               char *afilename2, char *buffer2, INT buffer_size2,
14416               char *afilename3, char *buffer3, INT buffer_size3, char *tag, INT tag_size)
14417 {
14418    if (rpc_is_remote())
14419       return rpc_call(RPC_EL_SUBMIT, run, author, type, system, subject,
14420                       text, reply_to, encoding,
14421                       afilename1, buffer1, buffer_size1,
14422                       afilename2, buffer2, buffer_size2,
14423                       afilename3, buffer3, buffer_size3, tag, tag_size);
14424 
14425 #ifdef LOCAL_ROUTINES
14426    {
14427       INT n, size, fh, status, run_number, mutex, buffer_size = 0, index, offset =
14428           0, tail_size = 0;
14429       struct tm *tms = NULL;
14430       char afilename[256], file_name[256], afile_name[3][256], dir[256], str[256],
14431           start_str[80], end_str[80], last[80], date[80], thread[80], attachment[256];
14432       HNDLE hDB;
14433       time_t now;
14434       char message[10000], *p, *buffer = NULL;
14435       BOOL bedit;
14436 
14437       cm_get_experiment_database(&hDB, NULL);
14438 
14439       bedit = (tag[0] != 0);
14440 
14441       /* request semaphore */
14442       cm_get_experiment_mutex(NULL, &mutex);
14443       status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
14444       if (status != SS_SUCCESS) {
14445          cm_msg(MERROR, "el_submit",
14446                 "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
14447          abort();
14448       }
14449 
14450       /* get run number from ODB if not given */
14451       if (run > 0)
14452          run_number = run;
14453       else {
14454          /* get run number */
14455          size = sizeof(run_number);
14456          status =
14457              db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT,
14458                           TRUE);
14459          assert(status == SUCCESS);
14460       }
14461 
14462       if (run_number < 0) {
14463          cm_msg(MERROR, "el_submit", "aborting on attempt to use invalid run number %d",
14464                 run_number);
14465          abort();
14466       }
14467 
14468       for (index = 0; index < 3; index++) {
14469          /* generate filename for attachment */
14470          afile_name[index][0] = file_name[0] = 0;
14471 
14472          if (index == 0) {
14473             strcpy(afilename, afilename1);
14474             buffer = buffer1;
14475             buffer_size = buffer_size1;
14476          } else if (index == 1) {
14477             strcpy(afilename, afilename2);
14478             buffer = buffer2;
14479             buffer_size = buffer_size2;
14480          } else if (index == 2) {
14481             strcpy(afilename, afilename3);
14482             buffer = buffer3;
14483             buffer_size = buffer_size3;
14484          }
14485 
14486          if (afilename[0]) {
14487             strcpy(file_name, afilename);
14488             p = file_name;
14489             while (strchr(p, ':'))
14490                p = strchr(p, ':') + 1;
14491             while (strchr(p, '\\'))
14492                p = strchr(p, '\\') + 1; /* NT */
14493             while (strchr(p, '/'))
14494                p = strchr(p, '/') + 1;  /* Unix */
14495             while (strchr(p, ']'))
14496                p = strchr(p, ']') + 1;  /* VMS */
14497 
14498             /* assemble ELog filename */
14499             if (p[0]) {
14500                dir[0] = 0;
14501                if (hDB > 0) {
14502                   size = sizeof(dir);
14503                   memset(dir, 0, size);
14504                   status =
14505                       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING,
14506                                    FALSE);
14507                   if (status != DB_SUCCESS)
14508                      db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING,
14509                                   TRUE);
14510 
14511                   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14512                      strcat(dir, DIR_SEPARATOR_STR);
14513                }
14514 #if !defined(OS_VXWORKS)
14515 #if !defined(OS_VMS)
14516                tzset();
14517 #endif
14518 #endif
14519 
14520                time((time_t *) & now);
14521                tms = localtime((const time_t *) &now);
14522 
14523                strcpy(str, p);
14524                sprintf(afile_name[index], "%02d%02d%02d_%02d%02d%02d_%s",
14525                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14526                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14527                sprintf(file_name, "%s%02d%02d%02d_%02d%02d%02d_%s", dir,
14528                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14529                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14530 
14531                /* save attachment */
14532                fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14533                if (fh < 0) {
14534                   cm_msg(MERROR, "el_submit", "Cannot write attachment file \"%s\"",
14535                          file_name);
14536                } else {
14537                   write(fh, buffer, buffer_size);
14538                   close(fh);
14539                }
14540             }
14541          }
14542       }
14543 
14544       /* generate new file name YYMMDD.log in data directory */
14545       cm_get_experiment_database(&hDB, NULL);
14546 
14547       size = sizeof(dir);
14548       memset(dir, 0, size);
14549       status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
14550       if (status != DB_SUCCESS)
14551          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
14552 
14553       if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14554          strcat(dir, DIR_SEPARATOR_STR);
14555 
14556 #if !defined(OS_VXWORKS)
14557 #if !defined(OS_VMS)
14558       tzset();
14559 #endif
14560 #endif
14561 
14562       if (bedit) {
14563          /* edit existing message */
14564          strcpy(str, tag);
14565          if (strchr(str, '.')) {
14566             offset = atoi(strchr(str, '.') + 1);
14567             *strchr(str, '.') = 0;
14568          }
14569          sprintf(file_name, "%s%s.log", dir, str);
14570          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14571          if (fh < 0) {
14572             ss_mutex_release(mutex);
14573             return EL_FILE_ERROR;
14574          }
14575          lseek(fh, offset, SEEK_SET);
14576          read(fh, str, 16);
14577          size = atoi(str + 9);
14578          read(fh, message, size);
14579 
14580          el_decode(message, "Date: ", date, sizeof(date));
14581          el_decode(message, "Thread: ", thread, sizeof(thread));
14582          el_decode(message, "Attachment: ", attachment, sizeof(attachment));
14583 
14584          /* buffer tail of logfile */
14585          lseek(fh, 0, SEEK_END);
14586          tail_size = TELL(fh) - (offset + size);
14587 
14588          if (tail_size > 0) {
14589             buffer = (char *) M_MALLOC(tail_size);
14590             if (buffer == NULL) {
14591                close(fh);
14592                ss_mutex_release(mutex);
14593                return EL_FILE_ERROR;
14594             }
14595 
14596             lseek(fh, offset + size, SEEK_SET);
14597             n = read(fh, buffer, tail_size);
14598          }
14599          lseek(fh, offset, SEEK_SET);
14600       } else {
14601          /* create new message */
14602          time((time_t *) & now);
14603          tms = localtime((const time_t *) &now);
14604 
14605          sprintf(file_name, "%s%02d%02d%02d.log", dir,
14606                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14607 
14608          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14609          if (fh < 0) {
14610             ss_mutex_release(mutex);
14611             return EL_FILE_ERROR;
14612          }
14613 
14614          strcpy(date, ctime(&now));
14615          date[24] = 0;
14616 
14617          if (reply_to[0])
14618             sprintf(thread, "%16s %16s", reply_to, "0");
14619          else
14620             sprintf(thread, "%16s %16s", "0", "0");
14621 
14622          lseek(fh, 0, SEEK_END);
14623       }
14624 
14625       /* compose message */
14626 
14627       sprintf(message, "Date: %s\n", date);
14628       sprintf(message + strlen(message), "Thread: %s\n", thread);
14629       sprintf(message + strlen(message), "Run: %d\n", run_number);
14630       sprintf(message + strlen(message), "Author: %s\n", author);
14631       sprintf(message + strlen(message), "Type: %s\n", type);
14632       sprintf(message + strlen(message), "System: %s\n", system);
14633       sprintf(message + strlen(message), "Subject: %s\n", subject);
14634 
14635       /* keep original attachment if edit and no new attachment */
14636       if (bedit && afile_name[0][0] == 0 && afile_name[1][0] == 0 &&
14637           afile_name[2][0] == 0)
14638          sprintf(message + strlen(message), "Attachment: %s", attachment);
14639       else {
14640          sprintf(message + strlen(message), "Attachment: %s", afile_name[0]);
14641          if (afile_name[1][0])
14642             sprintf(message + strlen(message), ",%s", afile_name[1]);
14643          if (afile_name[2][0])
14644             sprintf(message + strlen(message), ",%s", afile_name[2]);
14645       }
14646       sprintf(message + strlen(message), "\n");
14647 
14648       sprintf(message + strlen(message), "Encoding: %s\n", encoding);
14649       sprintf(message + strlen(message), "========================================\n");
14650       strcat(message, text);
14651 
14652       assert(strlen(message) < sizeof(message));        /* bomb out on array overrun. */
14653 
14654       size = 0;
14655       sprintf(start_str, "$Start$: %6d\n", size);
14656       sprintf(end_str, "$End$:   %6d\n\f", size);
14657 
14658       size = strlen(message) + strlen(start_str) + strlen(end_str);
14659 
14660       if (tag != NULL && !bedit)
14661          sprintf(tag, "%02d%02d%02d.%d", tms->tm_year % 100, tms->tm_mon + 1,
14662                  tms->tm_mday, (int) TELL(fh));
14663 
14664       /* size has to fit in 6 digits */
14665       assert(size < 999999);
14666 
14667       sprintf(start_str, "$Start$: %6d\n", size);
14668       sprintf(end_str, "$End$:   %6d\n\f", size);
14669 
14670       write(fh, start_str, strlen(start_str));
14671       write(fh, message, strlen(message));
14672       write(fh, end_str, strlen(end_str));
14673 
14674       if (bedit) {
14675          if (tail_size > 0) {
14676             n = write(fh, buffer, tail_size);
14677             M_FREE(buffer);
14678          }
14679 
14680          /* truncate file here */
14681 #ifdef OS_WINNT
14682          chsize(fh, TELL(fh));
14683 #else
14684          ftruncate(fh, TELL(fh));
14685 #endif
14686       }
14687 
14688       close(fh);
14689 
14690       /* if reply, mark original message */
14691       if (reply_to[0] && !bedit) {
14692          strcpy(last, reply_to);
14693          do {
14694             status = el_search_message(last, &fh, FALSE);
14695             if (status == EL_SUCCESS) {
14696                /* position to next thread location */
14697                lseek(fh, 72, SEEK_CUR);
14698                memset(str, 0, sizeof(str));
14699                read(fh, str, 16);
14700                lseek(fh, -16, SEEK_CUR);
14701 
14702                /* if no reply yet, set it */
14703                if (atoi(str) == 0) {
14704                   sprintf(str, "%16s", tag);
14705                   write(fh, str, 16);
14706                   close(fh);
14707                   break;
14708                } else {
14709                   /* if reply set, find last one in chain */
14710                   strcpy(last, strtok(str, " "));
14711                   close(fh);
14712                }
14713             } else
14714                /* stop on error */
14715                break;
14716 
14717          } while (TRUE);
14718       }
14719 
14720       /* release elog mutex */
14721       ss_mutex_release(mutex);
14722    }
14723 #endif                          /* LOCAL_ROUTINES */
14724 
14725    return EL_SUCCESS;
14726 }
14727 
14728 /**dox***************************************************************/
14729 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14730 
14731 /********************************************************************/
14732 INT el_search_message(char *tag, int *fh, BOOL walk)
14733 {
14734    int i, size, offset, direction, last, status;
14735    struct tm *tms, ltms;
14736    DWORD lt, ltime, lact;
14737    char str[256], file_name[256], dir[256];
14738    HNDLE hDB;
14739 
14740 #if !defined(OS_VXWORKS)
14741 #if !defined(OS_VMS)
14742    tzset();
14743 #endif
14744 #endif
14745 
14746    /* open file */
14747    cm_get_experiment_database(&hDB, NULL);
14748 
14749    size = sizeof(dir);
14750    memset(dir, 0, size);
14751    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
14752    if (status != DB_SUCCESS)
14753       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
14754 
14755    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14756       strcat(dir, DIR_SEPARATOR_STR);
14757 
14758    /* check tag for direction */
14759    direction = 0;
14760    if (strpbrk(tag, "+-")) {
14761       direction = atoi(strpbrk(tag, "+-"));
14762       *strpbrk(tag, "+-") = 0;
14763    }
14764 
14765    /* if tag is given, open file directly */
14766    if (tag[0]) {
14767       /* extract time structure from tag */
14768       tms = &ltms;
14769       memset(tms, 0, sizeof(struct tm));
14770       tms->tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
14771       tms->tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
14772       tms->tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
14773       tms->tm_hour = 12;
14774 
14775       if (tms->tm_year < 90)
14776          tms->tm_year += 100;
14777       ltime = lt = mktime(tms);
14778 
14779       strcpy(str, tag);
14780       if (strchr(str, '.')) {
14781          offset = atoi(strchr(str, '.') + 1);
14782          *strchr(str, '.') = 0;
14783       } else
14784          return EL_FILE_ERROR;
14785 
14786       do {
14787          tms = localtime((const time_t *) &ltime);
14788 
14789          sprintf(file_name, "%s%02d%02d%02d.log", dir,
14790                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14791          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
14792 
14793          if (*fh < 0) {
14794             if (!walk)
14795                return EL_FILE_ERROR;
14796 
14797             if (direction == -1)
14798                ltime -= 3600 * 24;      /* one day back */
14799             else
14800                ltime += 3600 * 24;      /* go forward one day */
14801 
14802             /* set new tag */
14803             tms = localtime((const time_t *) &ltime);
14804             sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1,
14805                     tms->tm_mday);
14806          }
14807 
14808          /* in forward direction, stop today */
14809          if (direction != -1 && ltime > (DWORD) time(NULL) + 3600 * 24)
14810             break;
14811 
14812          /* in backward direction, go back 10 years */
14813          if (direction == -1 && abs((INT) lt - (INT) ltime) > 3600 * 24 * 365 * 10)
14814             break;
14815 
14816       } while (*fh < 0);
14817 
14818       if (*fh < 0)
14819          return EL_FILE_ERROR;
14820 
14821       lseek(*fh, offset, SEEK_SET);
14822 
14823       /* check if start of message */
14824       i = read(*fh, str, 15);
14825       if (i <= 0) {
14826          close(*fh);
14827          return EL_FILE_ERROR;
14828       }
14829 
14830       if (strncmp(str, "$Start$: ", 9) != 0) {
14831          close(*fh);
14832          return EL_FILE_ERROR;
14833       }
14834 
14835       lseek(*fh, offset, SEEK_SET);
14836    }
14837 
14838    /* open most recent file if no tag given */
14839    if (tag[0] == 0) {
14840       time((long *) &lt);
14841       ltime = lt;
14842       do {
14843          tms = localtime((const time_t *) &ltime);
14844 
14845          sprintf(file_name, "%s%02d%02d%02d.log", dir,
14846                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14847          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
14848 
14849          if (*fh < 0)
14850             ltime -= 3600 * 24; /* one day back */
14851 
14852       } while (*fh < 0 && (INT) lt - (INT) ltime < 3600 * 24 * 365);
14853 
14854       if (*fh < 0)
14855          return EL_FILE_ERROR;
14856 
14857       /* remember tag */
14858       sprintf(tag, "%02d%02d%02d", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14859 
14860       lseek(*fh, 0, SEEK_END);
14861 
14862       sprintf(tag + strlen(tag), ".%d", (int) TELL(*fh));
14863    }
14864 
14865 
14866    if (direction == -1) {
14867       /* seek previous message */
14868 
14869       if (TELL(*fh) == 0) {
14870          /* go back one day */
14871          close(*fh);
14872 
14873          lt = ltime;
14874          do {
14875             lt -= 3600 * 24;
14876             tms = localtime((const time_t *) &lt);
14877             sprintf(str, "%02d%02d%02d.0",
14878                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14879 
14880             status = el_search_message(str, fh, FALSE);
14881 
14882          } while (status != EL_SUCCESS && (INT) ltime - (INT) lt < 3600 * 24 * 365);
14883 
14884          if (status != EL_SUCCESS)
14885             return EL_FIRST_MSG;
14886 
14887          /* adjust tag */
14888          strcpy(tag, str);
14889 
14890          /* go to end of current file */
14891          lseek(*fh, 0, SEEK_END);
14892       }
14893 
14894       /* read previous message size */
14895       lseek(*fh, -17, SEEK_CUR);
14896       i = read(*fh, str, 17);
14897       if (i <= 0) {
14898          close(*fh);
14899          return EL_FILE_ERROR;
14900       }
14901 
14902       if (strncmp(str, "$End$: ", 7) != 0) {
14903          close(*fh);
14904          return EL_FILE_ERROR;
14905       }
14906 
14907       /* make sure the input string to atoi() is zero-terminated:
14908        * $End$:      355garbage
14909        * 01234567890123456789 */
14910       str[15] = 0;
14911 
14912       size = atoi(str + 7);
14913       assert(size > 15);
14914 
14915       lseek(*fh, -size, SEEK_CUR);
14916 
14917       /* adjust tag */
14918       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
14919    }
14920 
14921    if (direction == 1) {
14922       /* seek next message */
14923 
14924       /* read current message size */
14925       last = TELL(*fh);
14926 
14927       i = read(*fh, str, 15);
14928       if (i <= 0) {
14929          close(*fh);
14930          return EL_FILE_ERROR;
14931       }
14932       lseek(*fh, -15, SEEK_CUR);
14933 
14934       if (strncmp(str, "$Start$: ", 9) != 0) {
14935          close(*fh);
14936          return EL_FILE_ERROR;
14937       }
14938 
14939       /* make sure the input string to atoi() is zero-terminated
14940        * $Start$:    606garbage
14941        * 01234567890123456789 */
14942       str[15] = 0;
14943 
14944       size = atoi(str + 9);
14945       assert(size > 15);
14946 
14947       lseek(*fh, size, SEEK_CUR);
14948 
14949       /* if EOF, goto next day */
14950       i = read(*fh, str, 15);
14951       if (i < 15) {
14952          close(*fh);
14953          time((long *) &lact);
14954 
14955          lt = ltime;
14956          do {
14957             lt += 3600 * 24;
14958             tms = localtime((const time_t *) &lt);
14959             sprintf(str, "%02d%02d%02d.0",
14960                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14961 
14962             status = el_search_message(str, fh, FALSE);
14963 
14964          } while (status != EL_SUCCESS && (INT) lt - (INT) lact < 3600 * 24);
14965 
14966          if (status != EL_SUCCESS)
14967             return EL_LAST_MSG;
14968 
14969          /* adjust tag */
14970          strcpy(tag, str);
14971 
14972          /* go to beginning of current file */
14973          lseek(*fh, 0, SEEK_SET);
14974       } else
14975          lseek(*fh, -15, SEEK_CUR);
14976 
14977       /* adjust tag */
14978       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
14979    }
14980 
14981    return EL_SUCCESS;
14982 }
14983 
14984 
14985 /********************************************************************/
14986 INT el_retrieve(char *tag, char *date, int *run, char *author, char *type,
14987                 char *system, char *subject, char *text, int *textsize,
14988                 char *orig_tag, char *reply_tag,
14989                 char *attachment1, char *attachment2, char *attachment3, char *encoding)
14990 /********************************************************************\
14991 
14992   Routine: el_retrieve
14993 
14994   Purpose: Retrieve an ELog entry by its message tab
14995 
14996   Input:
14997     char   *tag             tag in the form YYMMDD.offset
14998     int    *size            Size of text buffer
14999 
15000   Output:
15001     char   *tag             tag of retrieved message
15002     char   *date            Date/time of message recording
15003     int    *run             Run number
15004     char   *author          Message author
15005     char   *type            Message type
15006     char   *system          Message system
15007     char   *subject         Subject
15008     char   *text            Message text
15009     char   *orig_tag        Original message if this one is a reply
15010     char   *reply_tag       Reply for current message
15011     char   *attachment1/2/3 File attachment
15012     char   *encoding        Encoding of message
15013     int    *size            Actual message text size
15014 
15015   Function value:
15016     EL_SUCCESS              Successful completion
15017     EL_LAST_MSG             Last message in log
15018 
15019 \********************************************************************/
15020 {
15021    int size, fh = 0, offset, search_status, rd;
15022    char str[256], *p;
15023    char message[10000], thread[256], attachment_all[256];
15024 
15025    if (tag[0]) {
15026       search_status = el_search_message(tag, &fh, TRUE);
15027       if (search_status != EL_SUCCESS)
15028          return search_status;
15029    } else {
15030       /* open most recent message */
15031       strcpy(tag, "-1");
15032       search_status = el_search_message(tag, &fh, TRUE);
15033       if (search_status != EL_SUCCESS)
15034          return search_status;
15035    }
15036 
15037    /* extract message size */
15038    offset = TELL(fh);
15039    rd = read(fh, str, 15);
15040    assert(rd == 15);
15041 
15042    /* make sure the input string is zero-terminated before we call atoi() */
15043    str[15] = 0;
15044 
15045    /* get size */
15046    size = atoi(str + 9);
15047 
15048    assert(strncmp(str, "$Start$:", 8) == 0);
15049    assert(size > 15);
15050    assert(size < sizeof(message));
15051 
15052    memset(message, 0, sizeof(message));
15053 
15054    rd = read(fh, message, size);
15055    assert(rd > 0);
15056    assert((rd + 15 == size) || (rd == size));
15057 
15058    close(fh);
15059 
15060    /* decode message */
15061    if (strstr(message, "Run: ") && run)
15062       *run = atoi(strstr(message, "Run: ") + 5);
15063 
15064    el_decode(message, "Date: ", date, 80);      /* size from show_elog_submit_query() */
15065    el_decode(message, "Thread: ", thread, sizeof(thread));
15066    el_decode(message, "Author: ", author, 80);  /* size from show_elog_submit_query() */
15067    el_decode(message, "Type: ", type, 80);      /* size from show_elog_submit_query() */
15068    el_decode(message, "System: ", system, 80);  /* size from show_elog_submit_query() */
15069    el_decode(message, "Subject: ", subject, 256);       /* size from show_elog_submit_query() */
15070    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
15071    el_decode(message, "Encoding: ", encoding, 80);      /* size from show_elog_submit_query() */
15072 
15073    /* break apart attachements */
15074    if (attachment1 && attachment2 && attachment3) {
15075       attachment1[0] = attachment2[0] = attachment3[0] = 0;
15076       p = strtok(attachment_all, ",");
15077       if (p != NULL) {
15078          strcpy(attachment1, p);
15079          p = strtok(NULL, ",");
15080          if (p != NULL) {
15081             strcpy(attachment2, p);
15082             p = strtok(NULL, ",");
15083             if (p != NULL)
15084                strcpy(attachment3, p);
15085          }
15086       }
15087 
15088       assert(strlen(attachment1) < 256);        /* size from show_elog_submit_query() */
15089       assert(strlen(attachment2) < 256);        /* size from show_elog_submit_query() */
15090       assert(strlen(attachment3) < 256);        /* size from show_elog_submit_query() */
15091    }
15092 
15093    /* conver thread in reply-to and reply-from */
15094    if (orig_tag != NULL && reply_tag != NULL) {
15095       p = strtok(thread, " \r");
15096       if (p != NULL)
15097          strcpy(orig_tag, p);
15098       else
15099          strcpy(orig_tag, "");
15100       p = strtok(NULL, " \r");
15101       if (p != NULL)
15102          strcpy(reply_tag, p);
15103       else
15104          strcpy(reply_tag, "");
15105       if (atoi(orig_tag) == 0)
15106          orig_tag[0] = 0;
15107       if (atoi(reply_tag) == 0)
15108          reply_tag[0] = 0;
15109    }
15110 
15111    p = strstr(message, "========================================\n");
15112 
15113    if (text != NULL) {
15114       if (p != NULL) {
15115          p += 41;
15116          if ((int) strlen(p) >= *textsize) {
15117             strncpy(text, p, *textsize - 1);
15118             text[*textsize - 1] = 0;
15119             return EL_TRUNCATED;
15120          } else {
15121             strcpy(text, p);
15122 
15123             /* strip end tag */
15124             if (strstr(text, "$End$"))
15125                *strstr(text, "$End$") = 0;
15126 
15127             *textsize = strlen(text);
15128          }
15129       } else {
15130          text[0] = 0;
15131          *textsize = 0;
15132       }
15133    }
15134 
15135    if (search_status == EL_LAST_MSG)
15136       return EL_LAST_MSG;
15137 
15138    return EL_SUCCESS;
15139 }
15140 
15141 
15142 /********************************************************************/
15143 INT el_search_run(int run, char *return_tag)
15144 /********************************************************************\
15145 
15146   Routine: el_search_run
15147 
15148   Purpose: Find first message belonging to a specific run
15149 
15150   Input:
15151     int    run              Run number
15152 
15153   Output:
15154     char   *tag             tag of retrieved message
15155 
15156   Function value:
15157     EL_SUCCESS              Successful completion
15158     EL_LAST_MSG             Last message in log
15159 
15160 \********************************************************************/
15161 {
15162    int actual_run, fh, status;
15163    char tag[256];
15164 
15165    tag[0] = return_tag[0] = 0;
15166 
15167    do {
15168       /* open first message in file */
15169       strcat(tag, "-1");
15170       status = el_search_message(tag, &fh, TRUE);
15171       if (status == EL_FIRST_MSG)
15172          break;
15173       if (status != EL_SUCCESS)
15174          return status;
15175       close(fh);
15176 
15177       if (strchr(tag, '.') != NULL)
15178          strcpy(strchr(tag, '.'), ".0");
15179 
15180       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15181                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15182    } while (actual_run >= run);
15183 
15184    while (actual_run < run) {
15185       strcat(tag, "+1");
15186       status = el_search_message(tag, &fh, TRUE);
15187       if (status == EL_LAST_MSG)
15188          break;
15189       if (status != EL_SUCCESS)
15190          return status;
15191       close(fh);
15192 
15193       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15194                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15195    }
15196 
15197    strcpy(return_tag, tag);
15198 
15199    if (status == EL_LAST_MSG || status == EL_FIRST_MSG)
15200       return status;
15201 
15202    return EL_SUCCESS;
15203 }
15204 
15205 
15206 /********************************************************************/
15207 INT el_delete_message(char *tag)
15208 /********************************************************************\
15209 
15210   Routine: el_submit
15211 
15212   Purpose: Submit an ELog entry
15213 
15214   Input:
15215     char   *tag             Message tage
15216 
15217   Output:
15218     <none>
15219 
15220   Function value:
15221     EL_SUCCESS              Successful completion
15222 
15223 \********************************************************************/
15224 {
15225 #ifdef LOCAL_ROUTINES
15226    INT n, size, fh, mutex, offset = 0, tail_size, status;
15227    char dir[256], str[256], file_name[256];
15228    HNDLE hDB;
15229    char *buffer = NULL;
15230 
15231    cm_get_experiment_database(&hDB, NULL);
15232 
15233    /* request semaphore */
15234    cm_get_experiment_mutex(NULL, &mutex);
15235    status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
15236    if (status != SS_SUCCESS) {
15237       cm_msg(MERROR, "el_delete_message",
15238              "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
15239       abort();
15240    }
15241 
15242 
15243    /* generate file name YYMMDD.log in data directory */
15244    cm_get_experiment_database(&hDB, NULL);
15245 
15246    size = sizeof(dir);
15247    memset(dir, 0, size);
15248    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15249    if (status != DB_SUCCESS)
15250       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15251 
15252    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15253       strcat(dir, DIR_SEPARATOR_STR);
15254 
15255    strcpy(str, tag);
15256    if (strchr(str, '.')) {
15257       offset = atoi(strchr(str, '.') + 1);
15258       *strchr(str, '.') = 0;
15259    }
15260    sprintf(file_name, "%s%s.log", dir, str);
15261    fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15262    if (fh < 0) {
15263       ss_mutex_release(mutex);
15264       return EL_FILE_ERROR;
15265    }
15266    lseek(fh, offset, SEEK_SET);
15267    read(fh, str, 16);
15268    size = atoi(str + 9);
15269 
15270    /* buffer tail of logfile */
15271    lseek(fh, 0, SEEK_END);
15272    tail_size = TELL(fh) - (offset + size);
15273 
15274    if (tail_size > 0) {
15275       buffer = (char *) M_MALLOC(tail_size);
15276       if (buffer == NULL) {
15277          close(fh);
15278          ss_mutex_release(mutex);
15279          return EL_FILE_ERROR;
15280       }
15281 
15282       lseek(fh, offset + size, SEEK_SET);
15283       n = read(fh, buffer, tail_size);
15284    }
15285    lseek(fh, offset, SEEK_SET);
15286 
15287    if (tail_size > 0) {
15288       n = write(fh, buffer, tail_size);
15289       M_FREE(buffer);
15290    }
15291 
15292    /* truncate file here */
15293 #ifdef OS_WINNT
15294    chsize(fh, TELL(fh));
15295 #else
15296    ftruncate(fh, TELL(fh));
15297 #endif
15298 
15299    /* if file length gets zero, delete file */
15300    tail_size = lseek(fh, 0, SEEK_END);
15301    close(fh);
15302 
15303    if (tail_size == 0)
15304       remove(file_name);
15305 
15306    /* release elog mutex */
15307    ss_mutex_release(mutex);
15308 #endif                          /* LOCAL_ROUTINES */
15309 
15310    return EL_SUCCESS;
15311 }
15312 
15313 /**dox***************************************************************/
15314 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15315 
15316 /**dox***************************************************************/
15317                    /** @} *//* end of elfunctionc */
15318 
15319 /**dox***************************************************************/
15320 /** @addtogroup alfunctionc
15321  *  
15322  *  @{  */
15323 
15324 /**dox***************************************************************/
15325 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15326 
15327 /********************************************************************\
15328 *                                                                    *
15329 *                     Alarm functions                                *
15330 *                                                                    *
15331 \********************************************************************/
15332 
15333 /********************************************************************/
15334 BOOL al_evaluate_condition(char *condition, char *value)
15335 {
15336    HNDLE hDB, hkey;
15337    int i, j, index, size;
15338    KEY key;
15339    double value1, value2;
15340    char str[256], op[3], function[80];
15341    char data[10000];
15342    DWORD time;
15343 
15344    strcpy(str, condition);
15345    op[1] = op[2] = 0;
15346    value1 = value2 = 0;
15347    index = 0;
15348 
15349    /* find value and operator */
15350    for (i = strlen(str) - 1; i > 0; i--)
15351       if (strchr("<>=!", str[i]) != NULL)
15352          break;
15353    op[0] = str[i];
15354    value2 = atof(str + i + 1);
15355    str[i] = 0;
15356 
15357    if (i > 0 && strchr("<>=!", str[i - 1])) {
15358       op[1] = op[0];
15359       op[0] = str[--i];
15360       str[i] = 0;
15361    }
15362 
15363    i--;
15364    while (i > 0 && str[i] == ' ')
15365       i--;
15366    str[i + 1] = 0;
15367 
15368    /* check if function */
15369    function[0] = 0;
15370    if (str[i] == ')') {
15371       str[i--] = 0;
15372       if (strchr(str, '(')) {
15373          *strchr(str, '(') = 0;
15374          strcpy(function, str);
15375          for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
15376             str[j] = str[i];
15377          str[j] = 0;
15378          i = j - 1;
15379       }
15380    }
15381 
15382    /* find key */
15383    if (str[i] == ']') {
15384       str[i--] = 0;
15385       while (i > 0 && isdigit(str[i]))
15386          i--;
15387       index = atoi(str + i + 1);
15388       str[i] = 0;
15389    }
15390 
15391    cm_get_experiment_database(&hDB, NULL);
15392    db_find_key(hDB, 0, str, &hkey);
15393    if (!hkey) {
15394       cm_msg(MERROR, "al_evaluate_condition",
15395              "Cannot find key %s to evaluate alarm condition", str);
15396       if (value)
15397          strcpy(value, "unknown");
15398       return FALSE;
15399    }
15400 
15401    if (equal_ustring(function, "access")) {
15402       /* check key access time */
15403       db_get_key_time(hDB, hkey, &time);
15404       sprintf(str, "%ld", time);
15405       value1 = atof(str);
15406    } else {
15407       /* get key data and convert to double */
15408       db_get_key(hDB, hkey, &key);
15409       size = sizeof(data);
15410       db_get_data(hDB, hkey, data, &size, key.type);
15411       db_sprintf(str, data, size, index, key.type);
15412       value1 = atof(str);
15413    }
15414 
15415    /* return value */
15416    if (value)
15417       strcpy(value, str);
15418 
15419    /* now do logical operation */
15420    if (strcmp(op, "=") == 0)
15421       return value1 == value2;
15422    if (strcmp(op, "==") == 0)
15423       return value1 == value2;
15424    if (strcmp(op, "!=") == 0)
15425       return value1 != value2;
15426    if (strcmp(op, "<") == 0)
15427       return value1 < value2;
15428    if (strcmp(op, ">") == 0)
15429       return value1 > value2;
15430    if (strcmp(op, "<=") == 0)
15431       return value1 <= value2;
15432    if (strcmp(op, ">=") == 0)
15433       return value1 >= value2;
15434 
15435    return FALSE;
15436 }
15437 
15438 /**dox***************************************************************/
15439 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15440 
15441 /********************************************************************/
15442 /**
15443 Trigger a certain alarm.
15444 \code  ...
15445   lazy.alarm[0] = 0;
15446   size = sizeof(lazy.alarm);
15447   db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
15448 
15449   // trigger alarm if defined
15450   if (lazy.alarm[0])
15451     al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
15452   ...
15453 \endcode
15454 @param alarm_name Alarm name, defined in /alarms/alarms
15455 @param alarm_message Optional message which goes with alarm
15456 @param default_class If alarm is not yet defined under
15457                     /alarms/alarms/<alarm_name>, a new one
15458                     is created and this default class is used.
15459 @param cond_str String displayed in alarm condition
15460 @param type Alarm type, one of AT_xxx
15461 @return AL_SUCCESS, AL_INVALID_NAME
15462 */
15463 INT al_trigger_alarm(char *alarm_name, char *alarm_message, char *default_class,
15464                      char *cond_str, INT type)
15465 {
15466    if (rpc_is_remote())
15467       return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name, alarm_message,
15468                       default_class, cond_str, type);
15469 
15470 #ifdef LOCAL_ROUTINES
15471    {
15472       int status, size;
15473       HNDLE hDB, hkeyalarm;
15474       char str[256];
15475       ALARM alarm;
15476       BOOL flag;
15477       ALARM_ODB_STR(alarm_odb_str);
15478 
15479       cm_get_experiment_database(&hDB, NULL);
15480 
15481       /* check online mode */
15482       flag = TRUE;
15483       size = sizeof(flag);
15484       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
15485       if (!flag)
15486          return AL_SUCCESS;
15487 
15488       /* find alarm */
15489       sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15490       db_find_key(hDB, 0, str, &hkeyalarm);
15491       if (!hkeyalarm) {
15492          /* alarm must be an internal analyzer alarm, so create a default alarm */
15493          status = db_create_record(hDB, 0, str, strcomb(alarm_odb_str));
15494          db_find_key(hDB, 0, str, &hkeyalarm);
15495          if (!hkeyalarm) {
15496             cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record");
15497             return AL_ERROR_ODB;
15498          }
15499 
15500          if (default_class && default_class[0])
15501             db_set_value(hDB, hkeyalarm, "Alarm Class", default_class, 32, 1, TID_STRING);
15502          status = TRUE;
15503          db_set_value(hDB, hkeyalarm, "Active", &status, sizeof(status), 1, TID_BOOL);
15504       }
15505 
15506       /* set parameters for internal alarms */
15507       if (type != AT_EVALUATED && type != AT_PERIODIC) {
15508          db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
15509          strcpy(str, cond_str);
15510          db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
15511       }
15512 
15513       size = sizeof(alarm);
15514       status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15515       if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15516          /* make sure alarm record has right structure */
15517          db_check_record(hDB, hkeyalarm, "", strcomb(alarm_odb_str), TRUE);
15518 
15519          size = sizeof(alarm);
15520          status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15521          if (status != DB_SUCCESS) {
15522             cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record");
15523             return AL_ERROR_ODB;
15524          }
15525       }
15526 
15527       /* if internal alarm, check if active and check interval */
15528       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15529          /* check global alarm flag */
15530          flag = TRUE;
15531          size = sizeof(flag);
15532          db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL,
15533                       TRUE);
15534          if (!flag)
15535             return AL_SUCCESS;
15536 
15537          if (!alarm.active)
15538             return AL_SUCCESS;
15539 
15540          if ((INT) ss_time() - (INT) alarm.checked_last < alarm.check_interval)
15541             return AL_SUCCESS;
15542 
15543          /* now the alarm will be triggered, so save time */
15544          alarm.checked_last = ss_time();
15545       }
15546 
15547       /* write back alarm message for internal alarms */
15548       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15549          strncpy(alarm.alarm_message, alarm_message, 79);
15550          alarm.alarm_message[79] = 0;
15551       }
15552 
15553       /* now trigger alarm class defined in this alarm */
15554       if (alarm.alarm_class[0])
15555          al_trigger_class(alarm.alarm_class, alarm_message, alarm.triggered > 0);
15556 
15557       /* signal alarm being triggered */
15558       cm_asctime(str, sizeof(str));
15559 
15560       if (!alarm.triggered)
15561          strcpy(alarm.time_triggered_first, str);
15562 
15563       alarm.triggered++;
15564       strcpy(alarm.time_triggered_last, str);
15565 
15566       alarm.checked_last = ss_time();
15567 
15568       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
15569       if (status != DB_SUCCESS) {
15570          cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record");
15571          return AL_ERROR_ODB;
15572       }
15573 
15574    }
15575 #endif                          /* LOCAL_ROUTINES */
15576 
15577    return AL_SUCCESS;
15578 }
15579 
15580 /**dox***************************************************************/
15581 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15582 
15583 /********************************************************************/
15584 INT al_trigger_class(char *alarm_class, char *alarm_message, BOOL first)
15585 /********************************************************************\
15586 
15587   Routine: al_trigger_class
15588 
15589   Purpose: Trigger a certain alarm class
15590 
15591   Input:
15592     char   *alarm_class     Alarm class, must be defined in
15593                             /alarms/classes
15594     char   *alarm_message   Optional message which goes with alarm
15595     BOOL   first            TRUE if alarm is triggered first time
15596                             (used for elog)
15597 
15598   Output:
15599 
15600   Function value:
15601     AL_INVALID_NAME         Alarm class not defined
15602     AL_SUCCESS              Successful completion
15603 
15604 \********************************************************************/
15605 {
15606    int status, size, state;
15607    HNDLE hDB, hkeyclass;
15608    char str[256], command[256], tag[32];
15609    ALARM_CLASS ac;
15610 
15611    cm_get_experiment_database(&hDB, NULL);
15612 
15613    /* get alarm class */
15614    sprintf(str, "/Alarms/Classes/%s", alarm_class);
15615    db_find_key(hDB, 0, str, &hkeyclass);
15616    if (!hkeyclass) {
15617       cm_msg(MERROR, "al_trigger_class", "Alarm class %s not found in ODB", alarm_class);
15618       return AL_INVALID_NAME;
15619    }
15620 
15621    size = sizeof(ac);
15622    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
15623    if (status != DB_SUCCESS) {
15624       cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record");
15625       return AL_ERROR_ODB;
15626    }
15627 
15628    /* write system message */
15629    if (ac.write_system_message &&
15630        (INT) ss_time() - (INT) ac.system_message_last > ac.system_message_interval) {
15631       sprintf(str, "%s: %s", alarm_class, alarm_message);
15632       cm_msg(MTALK, "al_trigger_class", str);
15633       ac.system_message_last = ss_time();
15634    }
15635 
15636    /* write elog message on first trigger */
15637    if (ac.write_elog_message && first)
15638       el_submit(0, "Alarm system", "Alarm", "General", alarm_class, str,
15639                 "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, 32);
15640 
15641    /* execute command */
15642    if (ac.execute_command[0] &&
15643        ac.execute_interval > 0 &&
15644        (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
15645       sprintf(str, "%s: %s", alarm_class, alarm_message);
15646       sprintf(command, ac.execute_command, str);
15647       cm_msg(MINFO, "al_trigger_class", "Execute: %s", command);
15648       ss_system(command);
15649       ac.execute_last = ss_time();
15650    }
15651 
15652    /* stop run */
15653    if (ac.stop_run) {
15654       state = STATE_STOPPED;
15655       size = sizeof(state);
15656       db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
15657       if (state != STATE_STOPPED)
15658          cm_transition(TR_STOP, 0, NULL, 0, ASYNC, FALSE);
15659    }
15660 
15661    status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
15662    if (status != DB_SUCCESS) {
15663       cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
15664       return AL_ERROR_ODB;
15665    }
15666 
15667    return AL_SUCCESS;
15668 }
15669 
15670 
15671 /********************************************************************/
15672 INT al_reset_alarm(char *alarm_name)
15673 /********************************************************************\
15674 
15675   Routine: al_reset_alarm
15676 
15677   Purpose: Reset (acknowledge) alarm
15678 
15679   Input:
15680     char   *alarm_name      Alarm name, must be defined in /Alarms/Alarms
15681                             If NULL reset all alarms
15682 
15683   Output:
15684     <none>
15685 
15686   Function value:
15687     AL_INVALID_NAME         Alarm name not defined
15688     AL_RESET                Alarm was triggered and reset
15689     AL_SUCCESS              Successful completion
15690 
15691 \********************************************************************/
15692 {
15693    int status, size, i;
15694    HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
15695    KEY key;
15696    char str[256];
15697    ALARM alarm;
15698    ALARM_CLASS ac;
15699 
15700    cm_get_experiment_database(&hDB, NULL);
15701 
15702    if (alarm_name == NULL) {
15703       /* reset all alarms */
15704       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
15705       if (hkeyalarm) {
15706          for (i = 0;; i++) {
15707             db_enum_link(hDB, hkeyalarm, i, &hsubkey);
15708 
15709             if (!hsubkey)
15710                break;
15711 
15712             db_get_key(hDB, hsubkey, &key);
15713             al_reset_alarm(key.name);
15714          }
15715       }
15716       return AL_SUCCESS;
15717    }
15718 
15719    /* find alarm and alarm class */
15720    sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15721    db_find_key(hDB, 0, str, &hkeyalarm);
15722    if (!hkeyalarm) {
15723       cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB", alarm_name);
15724       return AL_INVALID_NAME;
15725    }
15726 
15727    size = sizeof(alarm);
15728    status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15729    if (status != DB_SUCCESS) {
15730       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
15731       return AL_ERROR_ODB;
15732    }
15733 
15734    sprintf(str, "/Alarms/Classes/%s", alarm.alarm_class);
15735    db_find_key(hDB, 0, str, &hkeyclass);
15736    if (!hkeyclass) {
15737       cm_msg(MERROR, "al_reset_alarm", "Alarm class %s not found in ODB",
15738              alarm.alarm_class);
15739       return AL_INVALID_NAME;
15740    }
15741 
15742    size = sizeof(ac);
15743    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
15744    if (status != DB_SUCCESS) {
15745       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
15746       return AL_ERROR_ODB;
15747    }
15748 
15749    if (alarm.triggered) {
15750       alarm.triggered = 0;
15751       alarm.time_triggered_first[0] = 0;
15752       alarm.time_triggered_last[0] = 0;
15753       alarm.checked_last = 0;
15754 
15755       ac.system_message_last = 0;
15756       ac.execute_last = 0;
15757 
15758       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
15759       if (status != DB_SUCCESS) {
15760          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
15761          return AL_ERROR_ODB;
15762       }
15763       status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
15764       if (status != DB_SUCCESS) {
15765          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
15766          return AL_ERROR_ODB;
15767       }
15768       return AL_RESET;
15769    }
15770 
15771    return AL_SUCCESS;
15772 }
15773 
15774 
15775 /********************************************************************/
15776 INT al_check()
15777 /********************************************************************\
15778 
15779   Routine: al_scan
15780 
15781   Purpose: Scan ODB alarams and programs
15782 
15783   Input:
15784 
15785   Output:
15786 
15787   Function value:
15788     AL_SUCCESS              Successful completion
15789 
15790 \********************************************************************/
15791 {
15792    if (rpc_is_remote())
15793       return rpc_call(RPC_AL_CHECK);
15794 
15795 #ifdef LOCAL_ROUTINES
15796    {
15797       INT i, status, size, mutex;
15798       HNDLE hDB, hkeyroot, hkey;
15799       KEY key;
15800       ALARM alarm;
15801       char str[256], value[256];
15802       DWORD now;
15803       PROGRAM_INFO program_info;
15804       BOOL flag;
15805 
15806       ALARM_CLASS_STR(alarm_class_str);
15807       ALARM_ODB_STR(alarm_odb_str);
15808       ALARM_PERIODIC_STR(alarm_periodic_str);
15809 
15810       cm_get_experiment_database(&hDB, NULL);
15811 
15812       if (hDB == 0)
15813          return AL_SUCCESS;     /* called from server not yet connected */
15814 
15815       /* check online mode */
15816       flag = TRUE;
15817       size = sizeof(flag);
15818       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
15819       if (!flag)
15820          return AL_SUCCESS;
15821 
15822       /* check global alarm flag */
15823       flag = TRUE;
15824       size = sizeof(flag);
15825       db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
15826       if (!flag)
15827          return AL_SUCCESS;
15828 
15829       /* request semaphore */
15830       cm_get_experiment_mutex(&mutex, NULL);
15831       status = ss_mutex_wait_for(mutex, 100);
15832       if (status != SS_SUCCESS)
15833          return SUCCESS;        /* someone else is doing alarm business */
15834 
15835       /* check ODB alarms */
15836       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15837       if (!hkeyroot) {
15838          /* create default ODB alarm */
15839          status =
15840              db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB", strcomb(alarm_odb_str));
15841          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15842          if (!hkeyroot) {
15843             ss_mutex_release(mutex);
15844             return SUCCESS;
15845          }
15846 
15847          status =
15848              db_create_record(hDB, 0, "/Alarms/Alarms/Demo periodic",
15849                               strcomb(alarm_periodic_str));
15850          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15851          if (!hkeyroot) {
15852             ss_mutex_release(mutex);
15853             return SUCCESS;
15854          }
15855 
15856          /* create default alarm classes */
15857          status =
15858              db_create_record(hDB, 0, "/Alarms/Classes/Alarm", strcomb(alarm_class_str));
15859          status =
15860              db_create_record(hDB, 0, "/Alarms/Classes/Warning",
15861                               strcomb(alarm_class_str));
15862          if (status != DB_SUCCESS) {
15863             ss_mutex_release(mutex);
15864             return SUCCESS;
15865          }
15866       }
15867 
15868       for (i = 0;; i++) {
15869          status = db_enum_key(hDB, hkeyroot, i, &hkey);
15870          if (status == DB_NO_MORE_SUBKEYS)
15871             break;
15872 
15873          db_get_key(hDB, hkey, &key);
15874 
15875          size = sizeof(alarm);
15876          status = db_get_record(hDB, hkey, &alarm, &size, 0);
15877          if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15878             /* make sure alarm record has right structure */
15879             db_check_record(hDB, hkey, "", strcomb(alarm_odb_str), TRUE);
15880             size = sizeof(alarm);
15881             status = db_get_record(hDB, hkey, &alarm, &size, 0);
15882             if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15883                cm_msg(MERROR, "al_check", "Cannot get alarm record");
15884                continue;
15885             }
15886          }
15887 
15888          /* check periodic alarm only when active */
15889          if (alarm.active &&
15890              alarm.type == AT_PERIODIC &&
15891              alarm.check_interval > 0 &&
15892              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
15893             /* if checked_last has not been set, set it to current time */
15894             if (alarm.checked_last == 0) {
15895                alarm.checked_last = ss_time();
15896                db_set_record(hDB, hkey, &alarm, size, 0);
15897             } else
15898                al_trigger_alarm(key.name, alarm.alarm_message, alarm.alarm_class, "",
15899                                 AT_PERIODIC);
15900          }
15901 
15902          /* check alarm only when active and not internal */
15903          if (alarm.active &&
15904              alarm.type == AT_EVALUATED &&
15905              alarm.check_interval > 0 &&
15906              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
15907             /* if condition is true, trigger alarm */
15908             if (al_evaluate_condition(alarm.condition, value)) {
15909                sprintf(str, alarm.alarm_message, value);
15910                al_trigger_alarm(key.name, str, alarm.alarm_class, "", AT_EVALUATED);
15911             } else {
15912                alarm.checked_last = ss_time();
15913                status = db_set_record(hDB, hkey, &alarm, sizeof(alarm), 0);
15914                if (status != DB_SUCCESS) {
15915                   cm_msg(MERROR, "al_check", "Cannot write back alarm record");
15916                   continue;
15917                }
15918             }
15919          }
15920       }
15921 
15922       /* check /programs alarms */
15923       db_find_key(hDB, 0, "/Programs", &hkeyroot);
15924       if (hkeyroot) {
15925          for (i = 0;; i++) {
15926             status = db_enum_key(hDB, hkeyroot, i, &hkey);
15927             if (status == DB_NO_MORE_SUBKEYS)
15928                break;
15929 
15930             db_get_key(hDB, hkey, &key);
15931 
15932             /* don't check "execute on xxx" */
15933             if (key.type != TID_KEY)
15934                continue;
15935 
15936             size = sizeof(program_info);
15937             status = db_get_record(hDB, hkey, &program_info, &size, 0);
15938             if (status != DB_SUCCESS) {
15939                cm_msg(MERROR, "al_check", "Cannot get program info record");
15940                continue;
15941             }
15942 
15943             now = ss_time();
15944 
15945             rpc_get_name(str);
15946             str[strlen(key.name)] = 0;
15947             if (!equal_ustring(str, key.name) &&
15948                 cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
15949                if (program_info.first_failed == 0)
15950                   program_info.first_failed = now;
15951 
15952                /* fire alarm when not running for more than what specified in check interval */
15953                if (now - program_info.first_failed >= program_info.check_interval / 1000) {
15954                   /* if not running and alarm calss defined, trigger alarm */
15955                   if (program_info.alarm_class[0]) {
15956                      sprintf(str, "Program %s is not running", key.name);
15957                      al_trigger_alarm(key.name, str, program_info.alarm_class,
15958                                       "Program not running", AT_PROGRAM);
15959                   }
15960 
15961                   /* auto restart program */
15962                   if (program_info.auto_restart && program_info.start_command[0]) {
15963                      ss_system(program_info.start_command);
15964                      program_info.first_failed = 0;
15965                      cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
15966                   }
15967                }
15968             } else
15969                program_info.first_failed = 0;
15970 
15971             db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
15972          }
15973       }
15974 
15975       ss_mutex_release(mutex);
15976    }
15977 #endif                          /* LOCAL_COUTINES */
15978 
15979    return SUCCESS;
15980 }
15981 
15982 /**dox***************************************************************/
15983 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15984 
15985                    /** @} *//* end of alfunctionc */
15986 
15987 /***** sKIP eb_xxx **************************************************/
15988 /**dox***************************************************************/
15989 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15990 /***** sKIP eb_xxx **************************************************/
15991 
15992 #if !defined(OS_VXWORKS)
15993 /********************************************************************\
15994 *                                                                    *
15995 *                 Event buffer functions                             *
15996 *                                                                    *
15997 \********************************************************************/
15998 
15999 /* PAA several modification in the eb_xxx()
16000    also new function eb_buffer_full()
16001 */
16002 static char *_event_ring_buffer = NULL;
16003 static INT _eb_size;
16004 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
16005 
16006 /********************************************************************/
16007 INT eb_create_buffer(INT size)
16008 /********************************************************************\
16009 
16010   Routine: eb_create_buffer
16011 
16012   Purpose: Create an event buffer. Has to be called initially before
16013            any other eb_xxx function
16014 
16015   Input:
16016     INT    size             Size in bytes
16017 
16018   Output:
16019     none
16020 
16021   Function value:
16022     CM_SUCCESS              Successful completion
16023     BM_NO_MEMEORY           Out of memory
16024 
16025 \********************************************************************/
16026 {
16027    _event_ring_buffer = (char *) M_MALLOC(size);
16028    if (_event_ring_buffer == NULL)
16029       return BM_NO_MEMORY;
16030 
16031    memset(_event_ring_buffer, 0, size);
16032    _eb_size = size;
16033 
16034    _eb_write_pointer = _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16035 
16036    _send_sock = rpc_get_event_sock();
16037 
16038    return CM_SUCCESS;
16039 }
16040 
16041 /********************************************************************/
16042 INT eb_free_buffer()
16043 /********************************************************************\
16044 
16045   Routine: eb_free_buffer
16046 
16047   Purpose: Free memory allocated voa eb_create_buffer
16048 
16049   Input:
16050     none
16051 
16052   Output:
16053     none
16054 
16055   Function value:
16056     CM_SUCCESS              Successful completion
16057 
16058 \********************************************************************/
16059 {
16060    if (_event_ring_buffer)
16061       M_FREE(_event_ring_buffer);
16062 
16063    _eb_size = 0;
16064    return CM_SUCCESS;
16065 }
16066 
16067 
16068 /********************************************************************/
16069 INT eb_free_space(void)
16070 /********************************************************************\
16071 
16072   Routine: eb_free_space
16073 
16074   Purpose: Compute and return usable free space in the event buffer
16075 
16076   Input:
16077     none
16078 
16079   Output:
16080     none
16081 
16082   Function value:
16083     INT    Number of usable free bytes in the event buffer
16084 
16085 \********************************************************************/
16086 {
16087    INT free;
16088 
16089    if (_event_ring_buffer == NULL) {
16090       cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
16091       return -1;
16092    }
16093 
16094    if (_eb_write_pointer >= _eb_read_pointer) {
16095       free = _eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer);
16096    } else if (_eb_write_pointer >= _event_ring_buffer) {
16097       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16098    } else if (_eb_end_pointer == _event_ring_buffer) {
16099       _eb_write_pointer = _event_ring_buffer;
16100       free = _eb_size;
16101    } else if (_eb_read_pointer == _event_ring_buffer) {
16102       free = 0;
16103    } else {
16104       _eb_write_pointer = _event_ring_buffer;
16105       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16106    }
16107 
16108    return free;
16109 }
16110 
16111 
16112 /********************************************************************/
16113 DWORD eb_get_level()
16114 /********************************************************************\
16115 
16116   Routine: eb_get_level
16117 
16118   Purpose: Return filling level of event buffer in percent
16119 
16120   Input:
16121     none
16122 
16123   Output:
16124     none
16125 
16126   Function value:
16127     DWORD level              0..99
16128 
16129 \********************************************************************/
16130 {
16131    INT size;
16132 
16133    size = _eb_size - eb_free_space();
16134 
16135    return (100 * size) / _eb_size;
16136 }
16137 
16138 
16139 /********************************************************************/
16140 BOOL eb_buffer_full(void)
16141 /********************************************************************\
16142 
16143   Routine: eb_buffer_full
16144 
16145   Purpose: Test if there is sufficient space in the event buffer
16146     for another event
16147 
16148   Input:
16149     none
16150 
16151   Output:
16152     none
16153 
16154   Function value:
16155     BOOL  Is there enough space for another event in the event buffer
16156 
16157 \********************************************************************/
16158 {
16159    INT free;
16160 
16161    free = eb_free_space();
16162 
16163    /* if max. event won't fit, return zero */
16164    return (free < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
16165 }
16166 
16167 
16168 /********************************************************************/
16169 EVENT_HEADER *eb_get_pointer()
16170 /********************************************************************\
16171 
16172   Routine: eb_get_pointer
16173 
16174   Purpose: Get pointer to next free location in event buffer
16175 
16176   Input:
16177     none
16178 
16179   Output:
16180     none
16181 
16182   Function value:
16183     EVENT_HEADER *            Pointer to free location
16184 
16185 \********************************************************************/
16186 {
16187    /* if max. event won't fit, return zero */
16188    if (eb_buffer_full()) {
16189 #ifdef OS_VXWORKS
16190       logMsg("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
16191              _eb_read_pointer - _event_ring_buffer,
16192              _eb_write_pointer - _event_ring_buffer,
16193              _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
16194 #endif
16195       return NULL;
16196    }
16197 
16198    /* leave space for buffer handle */
16199    return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
16200 }
16201 
16202 
16203 /********************************************************************/
16204 INT eb_increment_pointer(INT buffer_handle, INT event_size)
16205 /********************************************************************\
16206 
16207   Routine: eb_increment_pointer
16208 
16209   Purpose: Increment write pointer of event buffer after an event
16210            has been copied into the buffer (at an address previously
16211            obtained via eb_get_pointer)
16212 
16213   Input:
16214     INT buffer_handle         Buffer handle event should be sent to
16215     INT event_size            Event size in bytes including header
16216 
16217   Output:
16218     none
16219 
16220   Function value:
16221     CM_SUCCESS                Successful completion
16222 
16223 \********************************************************************/
16224 {
16225    INT aligned_event_size;
16226 
16227    /* if not connected remotely, use bm_send_event */
16228    if (_send_sock == 0)
16229       return bm_send_event(buffer_handle,
16230                            _eb_write_pointer + sizeof(INT), event_size, SYNC);
16231 
16232    aligned_event_size = ALIGN8(event_size);
16233 
16234    /* copy buffer handle */
16235    *((INT *) _eb_write_pointer) = buffer_handle;
16236    _eb_write_pointer += sizeof(INT) + aligned_event_size;
16237 
16238    if (_eb_write_pointer > _eb_end_pointer)
16239       _eb_end_pointer = _eb_write_pointer;
16240 
16241    if (_eb_write_pointer > _event_ring_buffer + _eb_size)
16242       cm_msg(MERROR, "eb_increment_pointer",
16243              "event size (%d) exeeds maximum event size (%d)", event_size,
16244              MAX_EVENT_SIZE);
16245 
16246    if (_eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer) <
16247        MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT)) {
16248       _eb_write_pointer = _event_ring_buffer;
16249 
16250       /* avoid rp==wp */
16251       if (_eb_read_pointer == _event_ring_buffer)
16252          _eb_write_pointer--;
16253    }
16254 
16255    return CM_SUCCESS;
16256 }
16257 
16258 
16259 /********************************************************************/
16260 INT eb_send_events(BOOL send_all)
16261 /********************************************************************\
16262 
16263   Routine: eb_send_events
16264 
16265   Purpose: Send events from the event buffer to the server
16266 
16267   Input:
16268     BOOL send_all             If FALSE, only send events if buffer
16269                               contains more than _opt_tcp_size bytes
16270 
16271   Output:
16272     none
16273 
16274   Function value:
16275     CM_SUCCESS                Successful completion
16276 
16277 \********************************************************************/
16278 {
16279    char *eb_wp, *eb_ep;
16280    INT size, i;
16281 
16282    /* write pointers are volatile, so make copy */
16283    eb_ep = _eb_end_pointer;
16284    eb_wp = _eb_write_pointer;
16285 
16286    if (eb_wp == _eb_read_pointer)
16287       return CM_SUCCESS;
16288    if (eb_wp > _eb_read_pointer) {
16289       size = (PTYPE) eb_wp - (PTYPE) _eb_read_pointer;
16290 
16291       /* don't send if less than optimal TCP buffer size available */
16292       if (size < (INT) _opt_tcp_size && !send_all)
16293          return CM_SUCCESS;
16294    } else {
16295       /* send last piece of event buffer */
16296       size = (PTYPE) eb_ep - (PTYPE) _eb_read_pointer;
16297    }
16298 
16299    while (size > _opt_tcp_size) {
16300       /* send buffer */
16301       i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
16302       if (i < 0) {
16303          printf("send_tcp() returned %d\n", i);
16304          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16305          return RPC_NET_ERROR;
16306       }
16307 
16308       _eb_read_pointer += _opt_tcp_size;
16309       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16310          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16311 
16312       size -= _opt_tcp_size;
16313    }
16314 
16315    if (send_all || eb_wp < _eb_read_pointer) {
16316       /* send buffer */
16317       i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
16318       if (i < 0) {
16319          printf("send_tcp() returned %d\n", i);
16320          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16321          return RPC_NET_ERROR;
16322       }
16323 
16324       _eb_read_pointer += size;
16325       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16326          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16327    }
16328 
16329    /* Check for case where eb_wp = eb_ring_buffer - 1 */
16330    if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
16331       return CM_SUCCESS;
16332    }
16333 
16334    if (eb_wp != _eb_read_pointer)
16335       return BM_MORE_EVENTS;
16336 
16337    return CM_SUCCESS;
16338 }
16339 
16340 #endif                          /* OS_VXWORKS  eb section */
16341 
16342 /**dox***************************************************************/
16343 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16344 
16345 /**dox***************************************************************/
16346 /** @addtogroup dmfunctionc
16347  *  
16348  *  @{  */
16349 
16350 /**dox***************************************************************/
16351 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16352 
16353 /********************************************************************\
16354 *                                                                    *
16355 *                 Dual memory buffer functions                       *
16356 *                                                                    *
16357 * Provide a dual memory buffer scheme for handling front-end         *
16358 * event. This code as been requested for allowing contemporary       *
16359 * task handling a)acquisition, b)network transfer if possible.       *
16360 * The pre-compiler switch will determine the mode of operation.      *
16361 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
16362 * a seperate task taking care of the dm_area_send                    *
16363 *                                                                    *
16364 * "*" : visible functions                                            *
16365 * dm_buffer_create():     *Setup the dual memory buffer              *
16366 *                          Setup semaphore                           *
16367 *                          Spawn second thread                       *
16368 * dm_buffer_release():    *Release memory allocation for dm          *
16369 *                          Force a kill of 2nd thread                *
16370 *                          Remove semaphore                          *
16371 * dm_area_full():         *Check for both area being full            *
16372 *                          None blocking, may be used for interrupt  *
16373 *                          disable.                                  *
16374 * dm_pointer_get()     :  *Check memory space and return pointer     *
16375 *                          Blocking function with timeout if no more *
16376 *                          space for next event. If error will abort.*
16377 * dm_pointer_increment(): *Move pointer to next free location        *
16378 *                          None blocking. performs bm_send_event if  *
16379 *                          local connection.                         *
16380 * dm_area_send():         *Transfer FULL buffer(s)                   *
16381 *                          None blocking function.                   *
16382 *                          if DUAL_THREAD: Give sem_send semaphore   *
16383 *                          else transfer FULL buffer                 *
16384 * dm_area_flush():        *Transfer all remaining events from dm     *
16385 *                          Blocking function with timeout            *
16386 *                          if DUAL_THREAD: Give sem_flush semaphore. *
16387 * dm_task():               Secondary thread handling DUAL_THREAD     *
16388 *                          mechanism. Serves 2 requests:             *
16389 *                          dm_send:  Transfer FULL buffer only.      *
16390 *                          dm_flush: Transfer ALL buffers.           *
16391 * dm_area_switch():        internal, used by dm_pointer_get()        *
16392 * dm_active_full():        internal: check space in current buffer   *
16393 * dm_buffer_send():        internal: send data for given area        *
16394 * dm_buffer_time_get():    interal: return the time stamp of the     *
16395 *                          last switch                               *
16396 \********************************************************************/
16397 
16398 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
16399 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
16400 #define DM_KILL        12       /* Kill request for 2nd thread */
16401 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
16402 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
16403 
16404 typedef struct {
16405    char *pt;                    /* top pointer    memory buffer          */
16406    char *pw;                    /* write pointer  memory buffer          */
16407    char *pe;                    /* end   pointer  memory buffer          */
16408    char *pb;                    /* bottom pointer memory buffer          */
16409    BOOL full;                   /* TRUE if memory buffer is full         */
16410    DWORD serial;                /* full buffer serial# for evt order     */
16411 } DMEM_AREA;
16412 
16413 typedef struct {
16414    DMEM_AREA *pa;               /* active memory buffer */
16415    DMEM_AREA area1;             /* mem buffer area 1 */
16416    DMEM_AREA area2;             /* mem buffer area 2 */
16417    DWORD serial;                /* overall buffer serial# for evt order     */
16418    INT action;                  /* for multi thread configuration */
16419    DWORD last_active;           /* switch time stamp */
16420    HNDLE sem_send;              /* semaphore for dm_task */
16421    HNDLE sem_flush;             /* semaphore for dm_task */
16422 } DMEM_BUFFER;
16423 
16424 DMEM_BUFFER dm;
16425 INT dm_user_max_event_size;
16426 
16427 /**dox***************************************************************/
16428 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16429 
16430 /********************************************************************/
16431 /**
16432 Setup a dual memory buffer. Has to be called initially before
16433            any other dm_xxx function
16434 @param size             Size in bytes
16435 @param user_max_event_size max event size
16436 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
16437 */
16438 INT dm_buffer_create(INT size, INT user_max_event_size)
16439 {
16440 
16441    dm.area1.pt = (char *) M_MALLOC(size);
16442    if (dm.area1.pt == NULL)
16443       return (BM_NO_MEMORY);
16444    dm.area2.pt = (char *) M_MALLOC(size);
16445    if (dm.area2.pt == NULL)
16446       return (BM_NO_MEMORY);
16447 
16448    /* check user event size against the system MAX_EVENT_SIZE */
16449    if (user_max_event_size > MAX_EVENT_SIZE) {
16450       cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
16451       return BM_MEMSIZE_MISMATCH;
16452    }
16453    dm_user_max_event_size = user_max_event_size;
16454 
16455    memset(dm.area1.pt, 0, size);
16456    memset(dm.area2.pt, 0, size);
16457 
16458    /* initialize pointers */
16459    dm.area1.pb = dm.area1.pt + size - 1024;
16460    dm.area1.pw = dm.area1.pe = dm.area1.pt;
16461    dm.area2.pb = dm.area2.pt + size - 1024;
16462    dm.area2.pw = dm.area2.pe = dm.area2.pt;
16463 
16464   /*-PAA-*/
16465 #ifdef DM_DEBUG
16466    printf(" in dm_buffer_create ---------------------------------\n");
16467    printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw, dm.area1.pe, dm.area1.pb);
16468    printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw, dm.area2.pe, dm.area2.pb);
16469 #endif
16470 
16471    /* activate first area */
16472    dm.pa = &dm.area1;
16473 
16474    /* Default not full */
16475    dm.area1.full = dm.area2.full = FALSE;
16476 
16477    /* Reset serial buffer number with proper starting sequence */
16478    dm.area1.serial = dm.area2.serial = 0;
16479    /* ensure proper serial on next increment */
16480    dm.serial = 1;
16481 
16482    /* set active buffer time stamp */
16483    dm.last_active = ss_millitime();
16484 
16485    /* get socket for event sending */
16486    _send_sock = rpc_get_event_sock();
16487 
16488 #ifdef DM_DUAL_THREAD
16489    {
16490       INT status;
16491       VX_TASK_SPAWN starg;
16492 
16493       /* create semaphore */
16494       status = ss_mutex_create("send", &dm.sem_send);
16495       if (status != SS_CREATED && status != SS_SUCCESS) {
16496          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
16497          return status;
16498       }
16499       status = ss_mutex_create("flush", &dm.sem_flush);
16500       if (status != SS_CREATED && status != SS_SUCCESS) {
16501          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
16502          return status;
16503       }
16504       /* spawn dm_task */
16505       memset(&starg, 0, sizeof(VX_TASK_SPAWN));
16506 
16507 #ifdef OS_VXWORKS
16508       /* Fill up the necessary arguments */
16509       strcpy(starg.name, "areaSend");
16510       starg.priority = 120;
16511       starg.stackSize = 20000;
16512 #endif
16513 
16514       if ((status = ss_thread_create(dm_task, (void *) &starg))
16515           != SS_SUCCESS) {
16516          cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
16517          return status;
16518       }
16519 #ifdef OS_WINNT
16520       /* necessary for true MUTEX (NT) */
16521       ss_mutex_wait_for(dm.sem_send, 0);
16522 #endif
16523    }
16524 #endif                          /* DM_DUAL_THREAD */
16525 
16526    return CM_SUCCESS;
16527 }
16528 
16529 /**dox***************************************************************/
16530 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16531 
16532 /********************************************************************/
16533 INT dm_buffer_release(void)
16534 /********************************************************************\
16535   Routine: dm_buffer_release
16536 
16537   Purpose: Release dual memory buffers
16538   Input:
16539     none
16540   Output:
16541     none
16542   Function value:
16543     CM_SUCCESS              Successful completion
16544 \********************************************************************/
16545 {
16546    if (dm.area1.pt) {
16547       free(dm.area1.pt);
16548       dm.area1.pt = NULL;
16549    }
16550    if (dm.area2.pt) {
16551       free(dm.area2.pt);
16552       dm.area2.pt = NULL;
16553    }
16554    dm.serial = 0;
16555    dm.area1.full = dm.area2.full = TRUE;
16556    dm.area1.serial = dm.area2.serial = 0;
16557 
16558 #ifdef DM_DUAL_THREAD
16559    /* kill spawned dm_task */
16560    dm.action = DM_KILL;
16561    ss_mutex_release(dm.sem_send);
16562    ss_mutex_release(dm.sem_flush);
16563 
16564    /* release semaphore */
16565    ss_mutex_delete(dm.sem_send, 0);
16566    ss_mutex_delete(dm.sem_flush, 0);
16567 #endif
16568 
16569    return CM_SUCCESS;
16570 }
16571 
16572 /********************************************************************/
16573 INLINE DMEM_AREA *dm_area_switch(void)
16574 /********************************************************************\
16575   Routine: dm_area_switch
16576 
16577   Purpose: set active area to the other empty area or NULL if both
16578            area are full. May have to check the serial consistancy...
16579   Input:
16580     none
16581   Output:
16582     none
16583   Function value:
16584     DMEM_AREA *            Pointer to active area or both full
16585 \********************************************************************/
16586 {
16587    volatile BOOL full1, full2;
16588 
16589    full1 = dm.area1.full;
16590    full2 = dm.area2.full;
16591 
16592    if (!full1 && !full2) {
16593       if (dm.area1.serial <= dm.area2.serial)
16594          return (&(dm.area1));
16595       else
16596          return (&(dm.area2));
16597    }
16598 
16599    if (!full1) {
16600       return (&(dm.area1));
16601    } else if (!full2) {
16602       return (&(dm.area2));
16603    }
16604    return (NULL);
16605 }
16606 
16607 /********************************************************************/
16608 INLINE BOOL dm_area_full(void)
16609 /********************************************************************\
16610   Routine: dm_area_full
16611 
16612   Purpose: Test if both area are full in order to block interrupt
16613   Input:
16614     none
16615   Output:
16616     none
16617   Function value:
16618     BOOL         TRUE if not enough space for another event
16619 \********************************************************************/
16620 {
16621    if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
16622       return TRUE;
16623    return FALSE;
16624 }
16625 
16626 /********************************************************************/
16627 INLINE BOOL dm_active_full(void)
16628 /********************************************************************\
16629   Routine: dm_active_full
16630 
16631   Purpose: Test if there is sufficient space in either event buffer
16632            for another event.
16633   Input:
16634     none
16635   Output:
16636     none
16637   Function value:
16638     BOOL         TRUE if not enough space for another event
16639 \********************************************************************/
16640 {
16641    /* catch both full areas, waiting for transfer */
16642    if (dm.pa == NULL)
16643       return TRUE;
16644    /* Check the space in the active buffer only
16645       as I don't switch buffer here */
16646    if (dm.pa->full)
16647       return TRUE;
16648    return (((PTYPE) dm.pa->pb - (PTYPE) dm.pa->pw) < (INT)
16649            (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
16650 }
16651 
16652 /********************************************************************/
16653 DWORD dm_buffer_time_get(void)
16654 /********************************************************************\
16655   Routine: dm_buffer_time_get
16656 
16657   Purpose: return the time from the last buffer switch.
16658 
16659   Input:
16660     none
16661   Output:
16662     none
16663   Function value:
16664     DWORD        time stamp
16665 
16666 \********************************************************************/
16667 {
16668    return (dm.last_active);
16669 }
16670 
16671 
16672 /********************************************************************/
16673 EVENT_HEADER *dm_pointer_get(void)
16674 /********************************************************************\
16675   Routine: dm_pointer_get
16676 
16677   Purpose: Get pointer to next free location in event buffer.
16678            after 10sec tries, it times out return NULL indicating a
16679            serious problem, i.e. abort.
16680   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
16681   Input:
16682     none
16683   Output:
16684     DM_BUFFER * dm    local valid dm to work on
16685   Function value:
16686     EVENT_HEADER *    Pointer to free location
16687     NULL              cannot after several attempt get free space => abort
16688 \********************************************************************/
16689 {
16690    int timeout, status;
16691 
16692    /* Is there still space in the active area ? */
16693    if (!dm_active_full())
16694       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
16695 
16696    /* no more space => switch area */
16697 
16698    /* Tag current area with global dm.serial for order consistency */
16699    dm.pa->serial = dm.serial++;
16700 
16701    /* set active buffer time stamp */
16702    dm.last_active = ss_millitime();
16703 
16704    /* mark current area full */
16705    dm.pa->full = TRUE;
16706 
16707    /* Trigger/do data transfer (Now/don't wait) */
16708    if ((status = dm_area_send()) == RPC_NET_ERROR) {
16709       cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
16710       return NULL;
16711    }
16712 
16713    /* wait switch completion (max 10 sec) */
16714    timeout = ss_millitime();    /* with timeout */
16715    while ((ss_millitime() - timeout) < 10000) {
16716       dm.pa = dm_area_switch();
16717       if (dm.pa != NULL)
16718          return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
16719       ss_sleep(200);
16720 #ifdef DM_DEBUG
16721       printf(" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
16722              ss_millitime() - timeout, dm.area1.full, dm.area2.full, dm.area1.serial,
16723              dm.area2.serial, dm.serial);
16724 #endif
16725    }
16726 
16727    /* Time running out abort */
16728    cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
16729    return NULL;
16730 }
16731 
16732 
16733 /********************************************************************/
16734 int dm_pointer_increment(INT buffer_handle, INT event_size)
16735 /********************************************************************\
16736   Routine: dm_pointer_increment
16737 
16738   Purpose: Increment write pointer of event buffer after an event
16739            has been copied into the buffer (at an address previously
16740            obtained via dm_pointer_get)
16741   Input:
16742     INT buffer_handle         Buffer handle event should be sent to
16743     INT event_size            Event size in bytes including header
16744   Output:
16745     none
16746   Function value:
16747     CM_SUCCESS                Successful completion
16748     status                    from bm_send_event for local connection
16749 \********************************************************************/
16750 {
16751    INT aligned_event_size;
16752 
16753    /* if not connected remotely, use bm_send_event */
16754    if (_send_sock == 0) {
16755       *((INT *) dm.pa->pw) = buffer_handle;
16756       return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT), event_size, SYNC);
16757    }
16758    aligned_event_size = ALIGN8(event_size);
16759 
16760    *((INT *) dm.pa->pw) = buffer_handle;
16761 
16762    /* adjust write pointer */
16763    dm.pa->pw += sizeof(INT) + aligned_event_size;
16764 
16765    /* adjust end pointer */
16766    dm.pa->pe = dm.pa->pw;
16767 
16768    return CM_SUCCESS;
16769 }
16770 
16771 /********************************************************************/
16772 INLINE INT dm_buffer_send(DMEM_AREA * larea)
16773 /********************************************************************\
16774   Routine: dm_buffer_send
16775 
16776   Purpose: Ship data to the cache in fact!
16777            Basically the same do loop is done in the send_tcp.
16778            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
16779            Introduced for bringing tcp option to user code.
16780   Input:
16781     DMEM_AREA * larea   The area to work with.
16782   Output:
16783     none
16784   Function value:
16785     CM_SUCCESS       Successful completion
16786     DM_ACTIVE_NULL   Both area were/are full
16787     RPC_NET_ERROR    send error
16788 \********************************************************************/
16789 {
16790    INT tot_size, nwrite;
16791    char *lpt;
16792 
16793    /* if not connected remotely, use bm_send_event */
16794    if (_send_sock == 0)
16795       return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
16796 
16797    /* alias */
16798    lpt = larea->pt;
16799 
16800    /* Get overall buffer size */
16801    tot_size = (PTYPE) larea->pe - (PTYPE) lpt;
16802 
16803    /* shortcut for speed */
16804    if (tot_size == 0)
16805       return CM_SUCCESS;
16806 
16807 #ifdef DM_DEBUG
16808    printf("lpt:%p size:%i ", lpt, tot_size);
16809 #endif
16810    nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
16811 #ifdef DM_DEBUG
16812    printf("nwrite:%i  errno:%i\n", nwrite, errno);
16813 #endif
16814    if (nwrite < 0)
16815       return RPC_NET_ERROR;
16816 
16817    /* reset area */
16818    larea->pw = larea->pe = larea->pt;
16819    larea->full = FALSE;
16820    return CM_SUCCESS;
16821 }
16822 
16823 /********************************************************************/
16824 INT dm_area_send(void)
16825 /********************************************************************\
16826   Routine: dm_area_send
16827 
16828   Purpose: Empty the FULL area only in proper event order
16829            Meant to be use either in mfe.c scheduler on every event
16830 
16831   Dual memory scheme:
16832    DM_DUAL_THREAD : Trigger sem_send
16833    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
16834                     if dm.pa is NULL (were both full) and now both are empty
16835 
16836   Input:
16837     none
16838   Output:
16839     none
16840   Function value:
16841     CM_SUCCESS                Successful completion
16842     RPC_NET_ERROR             send error
16843 \********************************************************************/
16844 {
16845 #ifdef DM_DUAL_THREAD
16846    INT status;
16847 
16848    /* force a DM_SEND if possible. Don't wait for completion */
16849    dm.action = DM_SEND;
16850    ss_mutex_release(dm.sem_send);
16851 #ifdef OS_WINNT
16852    /* necessary for true MUTEX (NT) */
16853    status = ss_mutex_wait_for(dm.sem_send, 1);
16854    if (status == SS_NO_MUTEX) {
16855       printf(" timeout while waiting for sem_send\n");
16856       return RPC_NET_ERROR;
16857    }
16858 #endif
16859 
16860    return CM_SUCCESS;
16861 #else
16862    /* ---------- NOT IN DUAL THREAD ----------- */
16863    INT status = 0;
16864 
16865    /* if no DUAL thread everything is local then */
16866    /* select the full area */
16867    if (dm.area1.full && dm.area2.full)
16868       if (dm.area1.serial <= dm.area2.serial)
16869          status = dm_buffer_send(&dm.area1);
16870       else
16871          status = dm_buffer_send(&dm.area2);
16872    else if (dm.area1.full)
16873       status = dm_buffer_send(&dm.area1);
16874    else if (dm.area2.full)
16875       status = dm_buffer_send(&dm.area2);
16876    if (status != CM_SUCCESS)
16877       return status;            /* catch transfer error too */
16878 
16879    if (dm.pa == NULL) {
16880       printf(" sync send dm.pa:%p full 1%li 2%li\n", dm.pa, dm.area1.full, dm.area2.full);
16881       dm.pa = &dm.area1;
16882    }
16883    return CM_SUCCESS;
16884 #endif
16885 }
16886 
16887 /********************************************************************/
16888 INT dm_task(void *pointer)
16889 /********************************************************************\
16890   Routine: dm_task
16891 
16892   Purpose: async send events doing a double purpose:
16893   a) send full buffer if found (DM_SEND) set by dm_active_full
16894   b) flush full areas (DM_FLUSH) set by dm_area_flush
16895   Input:
16896   none
16897   Output:
16898   none
16899   Function value:
16900   none
16901   \********************************************************************/
16902 {
16903 #ifdef DM_DUAL_THREAD
16904    INT status, timeout;
16905 
16906    printf("Semaphores initialization ... in areaSend ");
16907    /* Check or Wait for semaphore to be setup */
16908    timeout = ss_millitime();
16909    while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
16910       ss_sleep(200);
16911    if (dm.sem_send == 0)
16912       goto kill;
16913 
16914 #ifdef OS_WINNT
16915    /* necessary for true MUTEX (NT) get semaphore */
16916    ss_mutex_wait_for(dm.sem_flush, 0);
16917 #endif
16918 
16919    /* Main FOREVER LOOP */
16920    printf("task areaSend ready...\n");
16921    while (1) {
16922       if (!dm_area_full()) {
16923          /* wait semaphore here ........ 0 == forever */
16924          ss_mutex_wait_for(dm.sem_send, 0);
16925 #ifdef OS_WINNT
16926          /* necessary for true MUTEX (NT) give semaphore */
16927          ss_mutex_release(dm.sem_send);
16928 #endif
16929       }
16930       if (dm.action == DM_SEND) {
16931 #ifdef DM_DEBUG
16932          printf("Send %i %i ", dm.area1.full, dm.area2.full);
16933 #endif
16934          /* DM_SEND : Empty the oldest buffer only. */
16935          if (dm.area1.full && dm.area2.full) {
16936             if (dm.area1.serial <= dm.area2.serial)
16937                status = dm_buffer_send(&dm.area1);
16938             else
16939                status = dm_buffer_send(&dm.area2);
16940          } else if (dm.area1.full)
16941             status = dm_buffer_send(&dm.area1);
16942          else if (dm.area2.full)
16943             status = dm_buffer_send(&dm.area2);
16944 
16945          if (status != CM_SUCCESS) {
16946             cm_msg(MERROR, "dm_task", "network error %i", status);
16947             goto kill;
16948          }
16949       } /* if DM_SEND */
16950       else if (dm.action == DM_FLUSH) {
16951          /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
16952             events) Empty both area in order independently of being full or not */
16953          if (dm.area1.serial <= dm.area2.serial) {
16954             status = dm_buffer_send(&dm.area1);
16955             if (status != CM_SUCCESS)
16956                goto error;
16957             status = dm_buffer_send(&dm.area2);
16958             if (status != CM_SUCCESS)
16959                goto error;
16960          } else {
16961             status = dm_buffer_send(&dm.area2);
16962             if (status != CM_SUCCESS)
16963                goto error;
16964             status = dm_buffer_send(&dm.area1);
16965             if (status != CM_SUCCESS)
16966                goto error;
16967          }
16968          /* reset counter */
16969          dm.area1.serial = 0;
16970          dm.area2.serial = dm.serial = 1;
16971 #ifdef DM_DEBUG
16972          printf("dm.action: Flushing ...\n");
16973 #endif
16974          /* reset area to #1 */
16975          dm.pa = &dm.area1;
16976 
16977          /* release user */
16978          ss_mutex_release(dm.sem_flush);
16979 #ifdef OS_WINNT
16980          /* necessary for true MUTEX (NT) get semaphore back */
16981          ss_mutex_wait_for(dm.sem_flush, 0);
16982 #endif
16983       }
16984       /* if FLUSH */
16985       if (dm.action == DM_KILL)
16986          goto kill;
16987 
16988    }                            /* FOREVER (go back wainting for semaphore) */
16989 
16990    /* kill spawn now */
16991  error:
16992    cm_msg(MERROR, "dm_area_flush", "aSync Net error");
16993  kill:
16994    ss_mutex_release(dm.sem_flush);
16995 #ifdef OS_WINNT
16996    ss_mutex_wait_for(dm.sem_flush, 1);
16997 #endif
16998    cm_msg(MERROR, "areaSend", "task areaSend exiting now");
16999    exit;
17000    return 1;
17001 #else
17002    printf("DM_DUAL_THREAD not defined\n");
17003    return 0;
17004 #endif
17005 }
17006 
17007 /********************************************************************/
17008 INT dm_area_flush(void)
17009 /********************************************************************\
17010   Routine: dm_area_flush
17011 
17012   Purpose: Flush all the events in the areas.
17013            Used in mfe for BOR events, periodic events and
17014            if rate to low in main loop once a second. The standard
17015            data transfer should be done/triggered by dm_area_send (sync/async)
17016            in dm_pointer_get().
17017   Input:
17018     none
17019   Output:
17020     none
17021   Function value:
17022     CM_SUCCESS       Successful completion
17023     RPC_NET_ERROR    send error
17024 \********************************************************************/
17025 {
17026    INT status;
17027 #ifdef DM_DUAL_THREAD
17028    /* request FULL flush */
17029    dm.action = DM_FLUSH;
17030    ss_mutex_release(dm.sem_send);
17031 #ifdef OS_WINNT
17032    /* necessary for true MUTEX (NT) get semaphore back */
17033    ss_mutex_wait_for(dm.sem_send, 0);
17034 #endif
17035 
17036    /* important to wait for completion before continue with timeout
17037       timeout specified milliseconds */
17038    status = ss_mutex_wait_for(dm.sem_flush, 10000);
17039 #ifdef DM_DEBUG
17040    printf("dm_area_flush after waiting %i\n", status);
17041 #endif
17042 #ifdef OS_WINNT
17043    ss_mutex_release(dm.sem_flush);      /* give it back now */
17044 #endif
17045 
17046    return status;
17047 #else
17048    /* full flush done here */
17049    /* select in order both area independently of being full or not */
17050    if (dm.area1.serial <= dm.area2.serial) {
17051       status = dm_buffer_send(&dm.area1);
17052       if (status != CM_SUCCESS)
17053          return status;
17054       status = dm_buffer_send(&dm.area2);
17055       if (status != CM_SUCCESS)
17056          return status;
17057    } else {
17058       status = dm_buffer_send(&dm.area2);
17059       if (status != CM_SUCCESS)
17060          return status;
17061       status = dm_buffer_send(&dm.area1);
17062       if (status != CM_SUCCESS)
17063          return status;
17064    }
17065    /* reset serial counter */
17066    dm.area1.serial = dm.area2.serial = 0;
17067    dm.last_active = ss_millitime();
17068    return CM_SUCCESS;
17069 #endif
17070 }
17071 
17072 /********************************************************************/
17073 
17074 /**dox***************************************************************/
17075 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17076 
17077 /**dox***************************************************************/
17078                    /** @} *//* end of dmfunctionc */
17079 
17080 /**dox***************************************************************/
17081                    /** @} *//* end of midasincludecode */

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