midas.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 
00003   Name:         MIDAS.C
00004   Created by:   Stefan Ritt
00005 
00006   Contents:     MIDAS main library funcitons
00007 
00008   $Id: midas.c 3206 2006-07-31 22:17:05Z ritt $
00009 
00010 \********************************************************************/
00011 
00012 #include "midas.h"
00013 #include "msystem.h"
00014 #include "strlcpy.h"
00015 #include <assert.h>
00016 
00017 /**dox***************************************************************/
00018 /** @file midas.c
00019 The main core C-code for Midas.
00020 */
00021 
00022 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00023  */
00024 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00025  */
00026 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00027  */
00028 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00029  */
00030 /** @defgroup alfunctionc Midas Alarm Functions (al_xxx)
00031  */
00032 /** @defgroup hsfunctionc Midas History Functions (hs_xxx)
00033  */
00034 /** @defgroup elfunctionc Midas Elog Functions (el_xxx)
00035  */
00036 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00037  */
00038 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00039  */
00040 
00041 /**dox***************************************************************/
00042 /** @addtogroup midasincludecode
00043  *  
00044  *  @{  */
00045 
00046 /**dox***************************************************************/
00047 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00048 
00049 /********************************************************************/
00050 /* data type sizes */
00051 INT tid_size[] = {
00052    0,                           /* tid == 0 not defined                               */
00053    1,                           /* TID_BYTE      unsigned byte         0       255    */
00054    1,                           /* TID_SBYTE     signed byte         -128      127    */
00055    1,                           /* TID_CHAR      single character      0       255    */
00056    2,                           /* TID_WORD      two bytes             0      65535   */
00057    2,                           /* TID_SHORT     signed word        -32768    32767   */
00058    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00059    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00060    4,                           /* TID_BOOL      four bytes bool       0        1     */
00061    4,                           /* TID_FLOAT     4 Byte float format                  */
00062    8,                           /* TID_DOUBLE    8 Byte float format                  */
00063    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00064    0,                           /* TID_STRING    zero terminated string               */
00065    0,                           /* TID_ARRAY     variable length array of unkown type */
00066    0,                           /* TID_STRUCT    C structure                          */
00067    0,                           /* TID_KEY       key in online database               */
00068    0                            /* TID_LINK      link in online database              */
00069 };
00070 
00071 /* data type names */
00072 char *tid_name[] = {
00073    "NULL",
00074    "BYTE",
00075    "SBYTE",
00076    "CHAR",
00077    "WORD",
00078    "SHORT",
00079    "DWORD",
00080    "INT",
00081    "BOOL",
00082    "FLOAT",
00083    "DOUBLE",
00084    "BITFIELD",
00085    "STRING",
00086    "ARRAY",
00087    "STRUCT",
00088    "KEY",
00089    "LINK"
00090 };
00091 
00092 struct {
00093    int transition;
00094    char name[32];
00095 } trans_name[] = {
00096    {
00097    TR_START, "START",}, {
00098    TR_STOP, "STOP",}, {
00099    TR_PAUSE, "PAUSE",}, {
00100    TR_RESUME, "RESUME",}, {
00101    TR_DEFERRED, "DEFERRED",}, {
00102 0, "",},};
00103 
00104 /* Globals */
00105 #ifdef OS_MSDOS
00106 extern unsigned _stklen = 60000U;
00107 #endif
00108 
00109 extern DATABASE *_database;
00110 extern INT _database_entries;
00111 
00112 static BUFFER *_buffer;
00113 static INT _buffer_entries = 0;
00114 
00115 static INT _msg_buffer = 0;
00116 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00117 
00118 static REQUEST_LIST *_request_list;
00119 static INT _request_list_entries = 0;
00120 
00121 static EVENT_HEADER *_event_buffer;
00122 static INT _event_buffer_size = 0;
00123 
00124 static char *_net_recv_buffer;
00125 static INT _net_recv_buffer_size = 0;
00126 
00127 static char *_net_send_buffer;
00128 static INT _net_send_buffer_size = 0;
00129 
00130 static char *_tcp_buffer = NULL;
00131 static INT _tcp_wp = 0;
00132 static INT _tcp_rp = 0;
00133 
00134 static INT _send_sock;
00135 
00136 static void (*_debug_print) (char *) = NULL;
00137 static INT _debug_mode = 0;
00138 
00139 static INT _watchdog_last_called = 0;
00140 
00141 /* table for transition functions */
00142 
00143 typedef struct {
00144    INT transition;
00145    INT sequence_number;
00146     INT(*func) (INT, char *);
00147 } TRANS_TABLE;
00148 
00149 #define MAX_TRANSITIONS 20
00150 
00151 TRANS_TABLE _trans_table[MAX_TRANSITIONS];
00152 
00153 TRANS_TABLE _deferred_trans_table[] = {
00154    {TR_START},
00155    {TR_STOP},
00156    {TR_PAUSE},
00157    {TR_RESUME},
00158    {0}
00159 };
00160 
00161 static BOOL _server_registered = FALSE;
00162 
00163 static INT rpc_transition_dispatch(INT index, void *prpc_param[]);
00164 
00165 void cm_ctrlc_handler(int sig);
00166 
00167 typedef struct {
00168    INT code;
00169    char *string;
00170 } ERROR_TABLE;
00171 
00172 ERROR_TABLE _error_table[] = {
00173    {CM_WRONG_PASSWORD, "Wrong password"},
00174    {CM_UNDEF_EXP, "Experiment not defined"},
00175    {CM_UNDEF_ENVIRON,
00176     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00177    {RPC_NET_ERROR, "Cannot connect to remote host"},
00178    {0, NULL}
00179 };
00180 
00181 
00182 typedef struct {
00183    void *adr;
00184    int size;
00185    char file[80];
00186    int line;
00187 } DBG_MEM_LOC;
00188 
00189 DBG_MEM_LOC *_mem_loc = NULL;
00190 INT _n_mem = 0;
00191 
00192 void *dbg_malloc(unsigned int size, char *file, int line)
00193 {
00194    FILE *f;
00195    void *adr;
00196    int i;
00197 
00198    adr = malloc(size);
00199 
00200    /* search for deleted entry */
00201    for (i = 0; i < _n_mem; i++)
00202       if (_mem_loc[i].adr == NULL)
00203          break;
00204 
00205    if (i == _n_mem) {
00206       _n_mem++;
00207       if (!_mem_loc)
00208          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00209       else
00210          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00211    }
00212 
00213    _mem_loc[i].adr = adr;
00214    _mem_loc[i].size = size;
00215    strcpy(_mem_loc[i].file, file);
00216    _mem_loc[i].line = line;
00217 
00218    f = fopen("mem.txt", "w");
00219    for (i = 0; i < _n_mem; i++)
00220       if (_mem_loc[i].adr)
00221          fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
00222                  _mem_loc[i].size, _mem_loc[i].adr);
00223    fclose(f);
00224 
00225    return adr;
00226 }
00227 
00228 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00229 {
00230    void *adr;
00231 
00232    adr = dbg_malloc(size * count, file, line);
00233    if (adr)
00234       memset(adr, 0, size * count);
00235 
00236    return adr;
00237 }
00238 
00239 void dbg_free(void *adr, char *file, int line)
00240 {
00241    FILE *f;
00242    int i;
00243 
00244    free(adr);
00245 
00246    for (i = 0; i < _n_mem; i++)
00247       if (_mem_loc[i].adr == adr)
00248          break;
00249 
00250    if (i < _n_mem)
00251       _mem_loc[i].adr = NULL;
00252 
00253    f = fopen("mem.txt", "w");
00254    for (i = 0; i < _n_mem; i++)
00255       if (_mem_loc[i].adr)
00256          fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
00257                  _mem_loc[i].size, _mem_loc[i].adr);
00258    fclose(f);
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    } else if (message_type == MT_USER)
00575       sprintf(local_message, "[%s] ", routine);
00576 
00577    /* print argument list into message */
00578    va_start(argptr, format);
00579    vsprintf(str, (char *) format, argptr);
00580    va_end(argptr);
00581    strcat(send_message, str);
00582    strcat(local_message, str);
00583 
00584    /* call user function if set via cm_set_msg_print */
00585    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00586       _message_print(local_message);
00587 
00588    /* return if system mask is not set */
00589    if ((message_type & _message_mask_system) == 0) {
00590       in_routine = FALSE;
00591       return CM_SUCCESS;
00592    }
00593 
00594    /* copy message to event */
00595    pevent = (EVENT_HEADER *) event;
00596    strcpy(event + sizeof(EVENT_HEADER), send_message);
00597 
00598    /* send event if not of type MLOG */
00599    if (message_type != MT_LOG) {
00600       /* if no message buffer already opened, do so now */
00601       if (_msg_buffer == 0) {
00602          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00603          if (status != BM_SUCCESS && status != BM_CREATED) {
00604             in_routine = FALSE;
00605             return status;
00606          }
00607       }
00608 
00609       /* setup the event header and send the message */
00610       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00611                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00612       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00613    }
00614 
00615    /* log message */
00616    cm_msg_log(message_type, send_message);
00617 
00618    in_routine = FALSE;
00619 
00620    return CM_SUCCESS;
00621 }
00622 
00623 /********************************************************************/
00624 /**
00625 This routine is similar to @ref cm_msg().
00626 It differs from cm_msg() only by the logging destination being a file
00627 given through the argument list i.e:\b facility
00628 @internal
00629 @attention Do not add the "\n" escape carriage control at the end of the
00630 formated line as it is already added by the client on the receiving side.
00631 The first arg in the following example uses the predefined
00632 macro MINFO which handles automatically the first 3 arguments of the function
00633 (see @ref midas_macro).
00634 \code   ...
00635    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
00636    ...
00637 //----- File my_log_file.log
00638 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
00639 \endcode
00640 @param message_type See @ref midas_macro.
00641 @param filename Name of source file where error occured
00642 @param line Line number where error occured
00643 @param facility Logging file name
00644 @param routine Routine name
00645 @param format message to printout, ... Parameters like for printf()
00646 @return CM_SUCCESS
00647 */
00648 INT cm_msg1(INT message_type, char *filename, INT line,
00649             const char *facility, const char *routine, const char *format, ...)
00650 {
00651    va_list argptr;
00652    char event[1000], str[256], local_message[256], send_message[256], *pc;
00653    EVENT_HEADER *pevent;
00654    INT status;
00655    static BOOL in_routine = FALSE;
00656 
00657    /* avoid recursive calles */
00658    if (in_routine)
00659       return 0;
00660 
00661    in_routine = TRUE;
00662 
00663    /* strip path */
00664    pc = filename + strlen(filename);
00665    while (*pc != '\\' && *pc != '/' && pc != filename)
00666       pc--;
00667    if (pc != filename)
00668       pc++;
00669 
00670    /* print client name into string */
00671    if (message_type == MT_USER)
00672       sprintf(send_message, "[%s] ", routine);
00673    else {
00674       rpc_get_name(str);
00675       if (str[0])
00676          sprintf(send_message, "[%s] ", str);
00677       else
00678          send_message[0] = 0;
00679    }
00680 
00681    local_message[0] = 0;
00682 
00683    /* preceed error messages with file and line info */
00684    if (message_type == MT_ERROR) {
00685       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
00686       strcat(send_message, str);
00687       strcat(local_message, str);
00688    }
00689 
00690    /* print argument list into message */
00691    va_start(argptr, format);
00692    vsprintf(str, (char *) format, argptr);
00693    va_end(argptr);
00694 
00695    if (facility)
00696       sprintf(local_message + strlen(local_message), "{%s} ", facility);
00697 
00698    strcat(send_message, str);
00699    strcat(local_message, str);
00700 
00701    /* call user function if set via cm_set_msg_print */
00702    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00703       _message_print(local_message);
00704 
00705    /* return if system mask is not set */
00706    if ((message_type & _message_mask_system) == 0) {
00707       in_routine = FALSE;
00708       return CM_SUCCESS;
00709    }
00710 
00711    /* copy message to event */
00712    pevent = (EVENT_HEADER *) event;
00713    strcpy(event + sizeof(EVENT_HEADER), send_message);
00714 
00715    /* send event if not of type MLOG */
00716    if (message_type != MT_LOG) {
00717       /* if no message buffer already opened, do so now */
00718       if (_msg_buffer == 0) {
00719          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00720          if (status != BM_SUCCESS && status != BM_CREATED) {
00721             in_routine = FALSE;
00722             return status;
00723          }
00724       }
00725 
00726       /* setup the event header and send the message */
00727       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00728                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00729       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00730    }
00731 
00732    /* log message */
00733    cm_msg_log1(message_type, send_message, facility);
00734 
00735    in_routine = FALSE;
00736 
00737    return CM_SUCCESS;
00738 }
00739 
00740 /********************************************************************/
00741 /** 
00742 Register a dispatch function for receiving system messages.
00743 - example code from mlxspeaker.c
00744 \code
00745 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
00746 {
00747   char str[256], *pc, *sp;
00748   // print message
00749   printf("%s\n", (char *)(message));
00750 
00751   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
00752                  ,header->event_id
00753                  ,header->trigger_mask
00754                  ,header->serial_number
00755                  ,header->data_size);
00756   pc = strchr((char *)(message),']')+2;
00757   ...
00758   // skip none talking message
00759   if (header->trigger_mask == MT_TALK ||
00760       header->trigger_mask == MT_USER)
00761    ...
00762 }
00763 
00764 int main(int argc, char *argv[])
00765 {
00766   ...
00767   // now connect to server
00768   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
00769   if (status != CM_SUCCESS)
00770     return 1;
00771   // Register callback for messages
00772   cm_msg_register(receive_message);
00773   ...
00774 }
00775 \endcode
00776 @param func Dispatch function.
00777 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
00778 */
00779 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
00780 {
00781    INT status, id;
00782 
00783    /* if no message buffer already opened, do so now */
00784    if (_msg_buffer == 0) {
00785       status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00786       if (status != BM_SUCCESS && status != BM_CREATED)
00787          return status;
00788    }
00789 
00790    _msg_dispatch = func;
00791 
00792    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_SOME, &id, func);
00793 
00794    return status;
00795 }
00796 
00797 /********************************************************************/
00798 /**
00799 Retrieve old messages from log file 
00800 @param  n_message        Number of messages to retrieve
00801 @param  message          buf_size bytes of messages, separated
00802                          by \n characters. The returned number
00803                          of bytes is normally smaller than the
00804                          initial buf_size, since only full
00805                          lines are returned.
00806 @param *buf_size         Size of message buffer to fill
00807 @return CM_SUCCESS
00808 */
00809 INT cm_msg_retrieve(INT n_message, char *message, INT * buf_size)
00810 {
00811    char dir[256];
00812    char filename[256];
00813    char path[256], *p;
00814    FILE *f;
00815    INT status, size, offset, i;
00816    HNDLE hDB, hKey;
00817 
00818 
00819    if (rpc_is_remote())
00820       return rpc_call(RPC_CM_MSG_RETRIEVE, message, buf_size);
00821 
00822    cm_get_experiment_database(&hDB, NULL);
00823 
00824    if (hDB) {
00825       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00826       if (status == DB_SUCCESS) {
00827          size = sizeof(dir);
00828          memset(dir, 0, size);
00829          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00830          if (dir[0] != 0)
00831             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00832                strcat(dir, DIR_SEPARATOR_STR);
00833 
00834          strcpy(filename, "midas.log");
00835          size = sizeof(filename);
00836          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00837 
00838          strcpy(path, dir);
00839          strcat(path, filename);
00840       } else {
00841          cm_get_path(dir);
00842          if (dir[0] != 0)
00843             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00844                strcat(dir, DIR_SEPARATOR_STR);
00845 
00846          strcpy(path, dir);
00847          strcat(path, "midas.log");
00848       }
00849    } else
00850       strcpy(path, "midas.log");
00851 
00852    f = fopen(path, "rb");
00853    if (f == NULL) {
00854       sprintf(message, "Cannot open message log file %s\n", path);
00855       *buf_size = strlen(message);
00856       return CM_DB_ERROR;
00857    } else {
00858       /* position buf_size bytes before the EOF */
00859       fseek(f, -(*buf_size - 1), SEEK_END);
00860       offset = ftell(f);
00861       if (offset != 0) {
00862          /* go to end of line */
00863          fgets(message, *buf_size - 1, f);
00864          offset = ftell(f) - offset;
00865          *buf_size -= offset;
00866       }
00867 
00868       memset(message, 0, *buf_size);
00869       fread(message, 1, *buf_size - 1, f);
00870       message[*buf_size - 1] = 0;
00871       fclose(f);
00872 
00873       p = message + (*buf_size - 2);
00874 
00875       /* goto end of buffer */
00876       while (p != message && *p == 0)
00877          p--;
00878 
00879       /* strip line break */
00880       while (p != message && (*p == '\n' || *p == '\r'))
00881          *(p--) = 0;
00882 
00883       /* trim buffer so that last n_messages remain */
00884       for (i = 0; i < n_message; i++) {
00885          while (p != message && *p != '\n')
00886             p--;
00887 
00888          while (p != message && (*p == '\n' || *p == '\r'))
00889             p--;
00890       }
00891       if (p != message) {
00892          p++;
00893          while (*p == '\n' || *p == '\r')
00894             p++;
00895       }
00896 
00897       *buf_size = (*buf_size - 1) - (p - message);
00898 
00899       memmove(message, p, *buf_size);
00900       message[*buf_size] = 0;
00901    }
00902 
00903    return CM_SUCCESS;
00904 }
00905 
00906 /**dox***************************************************************/
00907                             /** @} *//* end of msgfunctionc */
00908 
00909 /**dox***************************************************************/
00910 /** @addtogroup cmfunctionc
00911  *  
00912  *  @{  */
00913 
00914 /********************************************************************/
00915 /**
00916 Get time from MIDAS server and set local time.
00917 @param    seconds         Time in seconds
00918 @return CM_SUCCESS
00919 */
00920 INT cm_synchronize(DWORD * seconds)
00921 {
00922    INT sec, status;
00923 
00924    /* if connected to server, get time from there */
00925    if (rpc_is_remote()) {
00926       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
00927 
00928       /* set local time */
00929       if (status == CM_SUCCESS)
00930          ss_settime(sec);
00931    }
00932 
00933    /* return time to caller */
00934    if (seconds != NULL) {
00935       *seconds = ss_time();
00936    }
00937 
00938    return CM_SUCCESS;
00939 }
00940 
00941 /********************************************************************/
00942 /**
00943 Get time from MIDAS server and set local time.
00944 @param    str            return time string
00945 @param    buf_size       Maximum size of str
00946 @return   CM_SUCCESS
00947 */
00948 INT cm_asctime(char *str, INT buf_size)
00949 {
00950    /* if connected to server, get time from there */
00951    if (rpc_is_remote())
00952       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
00953 
00954    /* return local time */
00955    strcpy(str, ss_asctime());
00956 
00957    return CM_SUCCESS;
00958 }
00959 
00960 /********************************************************************/
00961 /**
00962 Get time from ss_time on server.
00963 @param    time string
00964 @return   CM_SUCCESS
00965 */
00966 INT cm_time(DWORD * time)
00967 {
00968    /* if connected to server, get time from there */
00969    if (rpc_is_remote())
00970       return rpc_call(RPC_CM_TIME, time);
00971 
00972    /* return local time */
00973    *time = ss_time();
00974 
00975    return CM_SUCCESS;
00976 }
00977 
00978 /**dox***************************************************************/
00979                             /** @} *//* end of cmfunctionc */
00980 
00981 /********************************************************************\
00982 *                                                                    *
00983 *           cm_xxx  -  Common Functions to buffer & database         *
00984 *                                                                    *
00985 \********************************************************************/
00986 
00987 /* Globals */
00988 
00989 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
00990 static HNDLE _hDB = 0;          /* Database handle */
00991 static char _client_name[NAME_LENGTH];
00992 static char _path_name[MAX_STRING_LENGTH];
00993 static INT _call_watchdog = TRUE;
00994 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
00995 INT _mutex_alarm, _mutex_elog;
00996 
00997 /**dox***************************************************************/
00998 /** @addtogroup cmfunctionc
00999  *  
01000  *  @{  */
01001 
01002 /**
01003 Return version number of current MIDAS library as a string
01004 @return version number * 100
01005 */
01006 char *cm_get_version()
01007 {
01008    return MIDAS_VERSION;
01009 }
01010 
01011 /********************************************************************/
01012 /**
01013 Set path to actual experiment. This function gets called
01014 by cm_connect_experiment if the connection is established
01015 to a local experiment (not through the TCP/IP server).
01016 The path is then used for all shared memory routines.
01017 @param  path             Pathname
01018 @return CM_SUCCESS
01019 */
01020 INT cm_set_path(char *path)
01021 {
01022    strcpy(_path_name, path);
01023 
01024    /* check for trailing directory seperator */
01025    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01026       strcat(_path_name, DIR_SEPARATOR_STR);
01027 
01028    return CM_SUCCESS;
01029 }
01030 
01031 /********************************************************************/
01032 /**
01033 Return the path name previously set with cm_set_path.
01034 @param  path             Pathname
01035 @return CM_SUCCESS
01036 */
01037 INT cm_get_path(char *path)
01038 {
01039    strcpy(path, _path_name);
01040 
01041    return CM_SUCCESS;
01042 }
01043 
01044 /**dox***************************************************************/
01045                             /** @} *//* end of cmfunctionc */
01046 
01047 /**dox***************************************************************/
01048 /** @addtogroup cmfunctionc
01049  *  
01050  *  @{  */
01051 
01052 /**dox***************************************************************/
01053 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01054 
01055 typedef struct {
01056    char name[NAME_LENGTH];
01057    char directory[MAX_STRING_LENGTH];
01058    char user[NAME_LENGTH];
01059 } experiment_table;
01060 
01061 static experiment_table exptab[MAX_EXPERIMENT];
01062 
01063 /**dox***************************************************************/
01064 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01065 
01066 /**
01067 Scan the "exptab" file for MIDAS experiment names and save them
01068 for later use by rpc_server_accept(). The file is first searched
01069 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01070 @return CM_SUCCESS<br>
01071         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01072 */
01073 INT cm_scan_experiments(void)
01074 {
01075    INT i;
01076    FILE *f;
01077    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01078 
01079    for (i = 0; i < MAX_EXPERIMENT; i++)
01080       exptab[i].name[0] = 0;
01081 
01082    /* MIDAS_DIR overrides exptab */
01083    if (getenv("MIDAS_DIR")) {
01084       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01085 
01086       strcpy(exptab[0].name, "Default");
01087       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01088       exptab[0].user[0] = 0;
01089 
01090       return CM_SUCCESS;
01091    }
01092 
01093    /* default directory for different OSes */
01094 #if defined (OS_WINNT)
01095    if (getenv("SystemRoot"))
01096       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01097    else if (getenv("windir"))
01098       strlcpy(str, getenv("windir"), sizeof(str));
01099    else
01100       strcpy(str, "");
01101 
01102    strcpy(alt_str, str);
01103    strcat(str, "\\system32\\exptab");
01104    strcat(alt_str, "\\system\\exptab");
01105 #elif defined (OS_UNIX)
01106    strcpy(str, "/etc/exptab");
01107    strcpy(alt_str, "/exptab");
01108 #else
01109    strcpy(str, "exptab");
01110    strcpy(alt_str, "exptab");
01111 #endif
01112 
01113    /* MIDAS_EXPTAB overrides default directory */
01114    if (getenv("MIDAS_EXPTAB")) {
01115       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01116       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01117    }
01118 
01119    /* read list of available experiments */
01120    f = fopen(str, "r");
01121    if (f == NULL) {
01122       f = fopen(alt_str, "r");
01123       if (f == NULL)
01124          return CM_UNDEF_ENVIRON;
01125    }
01126 
01127    i = 0;
01128    if (f != NULL) {
01129       do {
01130          str[0] = 0;
01131          if (fgets(str, 100, f) == NULL)
01132             break;
01133          if (str[0] && str[0] != '#') {
01134             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01135 
01136             /* check for trailing directory separator */
01137             pdir = exptab[i].directory;
01138             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01139                strcat(pdir, DIR_SEPARATOR_STR);
01140 
01141             i++;
01142          }
01143       } while (!feof(f));
01144       fclose(f);
01145    }
01146 
01147    /*
01148       for (j=0 ; j<i ; j++)
01149       {
01150       sprintf(str, "Scanned experiment %s", exptab[j].name);
01151       cm_msg(MINFO, str);
01152       }
01153     */
01154 
01155    return CM_SUCCESS;
01156 }
01157 
01158 /********************************************************************/
01159 /**
01160 Delete client info from database
01161 @param hDB               Database handle
01162 @param pid               PID of entry to delete, zero for this process.
01163 @return CM_SUCCESS
01164 */
01165 INT cm_delete_client_info(HNDLE hDB, INT pid)
01166 {
01167 #ifdef LOCAL_ROUTINES
01168 
01169    /* only do it if local */
01170    if (!rpc_is_remote()) {
01171       INT status;
01172       HNDLE hKey;
01173       char str[256];
01174 
01175       if (!pid)
01176          pid = ss_gettid();
01177 
01178       /* don't delete info from a closed database */
01179       if (_database_entries == 0)
01180          return CM_SUCCESS;
01181 
01182       /* make operation atomic by locking database */
01183       db_lock_database(hDB);
01184 
01185       sprintf(str, "System/Clients/%0d", pid);
01186       status = db_find_key1(hDB, 0, str, &hKey);
01187       if (status != DB_SUCCESS) {
01188          db_unlock_database(hDB);
01189          return status;
01190       }
01191 
01192       /* unlock client entry and delete it without locking DB */
01193       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01194       db_delete_key1(hDB, hKey, 1, TRUE);
01195 
01196       db_unlock_database(hDB);
01197 
01198       /* touch notify key to inform others */
01199       status = 0;
01200       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01201    }
01202 #endif                          /*LOCAL_ROUTINES */
01203 
01204    return CM_SUCCESS;
01205 }
01206 
01207 /********************************************************************/
01208 /**
01209 Check if a client with a /system/client/xxx entry has
01210 a valid entry in the ODB client table. If not, remove
01211 that client from the /system/client tree. 
01212 @param   hDB               Handle to online database
01213 @param   hKeyClient        Handle to client key
01214 @return  CM_SUCCESS, CM_NO_CLIENT
01215 */
01216 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01217 {
01218 #ifdef LOCAL_ROUTINES
01219 
01220    KEY key;
01221    DATABASE_HEADER *pheader;
01222    DATABASE_CLIENT *pclient;
01223    INT i, client_pid, status;
01224    char name[NAME_LENGTH];
01225 
01226    db_get_key(hDB, hKeyClient, &key);
01227    client_pid = atoi(key.name);
01228 
01229    i = sizeof(name);
01230    db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01231 
01232    db_lock_database(hDB);
01233    if (_database[hDB - 1].attached) {
01234       pheader = _database[hDB - 1].database_header;
01235       pclient = pheader->client;
01236 
01237       /* loop through clients */
01238       for (i = 0; i < pheader->max_client_index; i++, pclient++)
01239          if (pclient->tid == client_pid)
01240             break;
01241 
01242       if (i == pheader->max_client_index) {
01243          /* client not found : delete ODB stucture */
01244          db_unlock_database(hDB);
01245 
01246          status = cm_delete_client_info(hDB, client_pid);
01247          if (status != CM_SUCCESS)
01248             cm_msg(MERROR, "cm_check_client", "cannot delete client info");
01249          else
01250             cm_msg(MINFO, "cm_check_clinet",
01251                    "Deleted /System/Clients/%d entry for client %s\n", client_pid, name);
01252 
01253          return CM_NO_CLIENT;
01254       }
01255    }
01256 
01257    db_unlock_database(hDB);
01258 
01259 #endif                          /*LOCAL_ROUTINES */
01260 
01261    return CM_SUCCESS;
01262 }
01263 
01264 /********************************************************************/
01265 /**
01266 Set client information in online database and return handle 
01267 @param  hDB              Handle to online database  
01268 @param  hKeyClient       returned key
01269 @param  host_name        server name 
01270 @param  client_name      Name of this program as it will be seen
01271                          by other clients.
01272 @param  hw_type          Type of byte order
01273 @param  password         MIDAS password  
01274 @param  watchdog_timeout Default watchdog timeout, can be overwritten
01275                          by ODB setting /programs/<name>/Watchdog timeout
01276 @return   CM_SUCCESS
01277 */
01278 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
01279                        char *client_name, INT hw_type, char *password,
01280                        DWORD watchdog_timeout)
01281 {
01282    if (rpc_is_remote())
01283       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
01284                       host_name, client_name, hw_type, password, watchdog_timeout);
01285 
01286 #ifdef LOCAL_ROUTINES
01287    {
01288       INT status, pid, data, i, index, size;
01289       HNDLE hKey, hSubkey;
01290       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
01291       BOOL call_watchdog, allow;
01292       PROGRAM_INFO_STR(program_info_str);
01293 
01294       /* check security if password is presend */
01295       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
01296       if (hKey) {
01297          /* get password */
01298          size = sizeof(pwd);
01299          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
01300 
01301          /* first check allowed hosts list */
01302          allow = FALSE;
01303          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
01304          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
01305             allow = TRUE;
01306 
01307          /* check allowed programs list */
01308          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
01309          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
01310             allow = TRUE;
01311 
01312          /* now check password */
01313          if (!allow &&
01314              strcmp(password, pwd) != 0 && strcmp(password, "mid7qBxsNMHux") != 0) {
01315             if (password[0])
01316                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s",
01317                       host_name);
01318             db_close_all_databases();
01319             bm_close_all_buffers();
01320             _msg_buffer = 0;
01321             return CM_WRONG_PASSWORD;
01322          }
01323       }
01324 
01325       /* make following operation atomic by locking database */
01326       db_lock_database(hDB);
01327 
01328       /* check if entry with this pid exists already */
01329       pid = ss_gettid();
01330 
01331       sprintf(str, "System/Clients/%0d", pid);
01332       status = db_find_key(hDB, 0, str, &hKey);
01333       if (status == DB_SUCCESS) {
01334          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
01335          db_delete_key(hDB, hKey, TRUE);
01336       }
01337 
01338       if (strlen(client_name) >= NAME_LENGTH)
01339          client_name[NAME_LENGTH] = 0;
01340 
01341       strcpy(name, client_name);
01342       strcpy(orig_name, client_name);
01343 
01344       /* check if client name already exists */
01345       status = db_find_key(hDB, 0, "System/Clients", &hKey);
01346 
01347       for (index = 1; status != DB_NO_MORE_SUBKEYS; index++) {
01348          for (i = 0;; i++) {
01349             status = db_enum_key(hDB, hKey, i, &hSubkey);
01350             if (status == DB_NO_MORE_SUBKEYS)
01351                break;
01352 
01353             if (status == DB_SUCCESS) {
01354                size = sizeof(str);
01355                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
01356             }
01357 
01358             /* check if client is living */
01359             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
01360                continue;
01361 
01362             if (equal_ustring(str, name)) {
01363                sprintf(name, "%s%d", client_name, index);
01364                break;
01365             }
01366          }
01367       }
01368 
01369       /* set name */
01370       sprintf(str, "System/Clients/%0d/Name", pid);
01371       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
01372       if (status != DB_SUCCESS) {
01373          db_unlock_database(hDB);
01374          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
01375          return status;
01376       }
01377 
01378       /* copy new client name */
01379       strcpy(client_name, name);
01380       db_set_client_name(hDB, client_name);
01381 
01382       /* set also as rpc name */
01383       rpc_set_name(client_name);
01384 
01385       /* use /system/clients/PID as root */
01386       sprintf(str, "System/Clients/%0d", pid);
01387       db_find_key(hDB, 0, str, &hKey);
01388 
01389       /* set host name */
01390       status =
01391           db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
01392       if (status != DB_SUCCESS) {
01393          db_unlock_database(hDB);
01394          return status;
01395       }
01396 
01397       /* set computer id */
01398       status = db_set_value(hDB, hKey, "Hardware type", &hw_type,
01399                             sizeof(hw_type), 1, TID_INT);
01400       if (status != DB_SUCCESS) {
01401          db_unlock_database(hDB);
01402          return status;
01403       }
01404 
01405       /* set server port */
01406       data = 0;
01407       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
01408       if (status != DB_SUCCESS) {
01409          db_unlock_database(hDB);
01410          return status;
01411       }
01412 
01413       /* lock client entry */
01414       db_set_mode(hDB, hKey, MODE_READ, TRUE);
01415 
01416       /* get (set) default watchdog timeout */
01417       size = sizeof(watchdog_timeout);
01418       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
01419       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01420 
01421       /* define /programs entry */
01422       sprintf(str, "/Programs/%s", orig_name);
01423       db_create_record(hDB, 0, str, strcomb(program_info_str));
01424 
01425       /* save handle for ODB and client */
01426       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
01427       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
01428 
01429       /* save watchdog timeout */
01430       cm_get_watchdog_params(&call_watchdog, NULL);
01431       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01432       if (call_watchdog)
01433          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
01434 
01435       /* end of atomic operations */
01436       db_unlock_database(hDB);
01437 
01438       /* touch notify key to inform others */
01439       data = 0;
01440       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
01441 
01442       *hKeyClient = hKey;
01443    }
01444 #endif                          /* LOCAL_ROUTINES */
01445 
01446    return CM_SUCCESS;
01447 }
01448 
01449 /********************************************************************/
01450 /**
01451 Get info about the current client 
01452 @param  *client_name       Client name.  
01453 @return   CM_SUCCESS, CM_UNDEF_EXP  
01454 */
01455 INT cm_get_client_info(char *client_name)
01456 {
01457    INT status, length;
01458    HNDLE hDB, hKey;
01459 
01460    /* get root key of client */
01461    cm_get_experiment_database(&hDB, &hKey);
01462    if (!hDB) {
01463       client_name[0] = 0;
01464       return CM_UNDEF_EXP;
01465    }
01466 
01467    status = db_find_key(hDB, hKey, "Name", &hKey);
01468    if (status != DB_SUCCESS)
01469       return status;
01470 
01471    length = NAME_LENGTH;
01472    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
01473    if (status != DB_SUCCESS)
01474       return status;
01475 
01476    return CM_SUCCESS;
01477 }
01478 
01479 /********************************************************************/
01480 /**
01481 Returns MIDAS environment variables. 
01482 @attention This function can be used to evaluate the standard MIDAS
01483            environment variables before connecting to an experiment
01484            (see @ref Environment_variables).
01485            The usual way is that the host name and experiment name are first derived
01486            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
01487            They can then be superseded by command line parameters with -h and -e flags.
01488 \code
01489 #include <stdio.h>
01490 #include <midas.h>
01491 main(int argc, char *argv[])
01492 {
01493   INT  status, i;
01494   char host_name[256],exp_name[32];
01495 
01496   // get default values from environment
01497   cm_get_environment(host_name, exp_name);
01498 
01499   // parse command line parameters
01500   for (i=1 ; i<argc ; i++)
01501     {
01502     if (argv[i][0] == '-')
01503       {
01504       if (i+1 >= argc || argv[i+1][0] == '-')
01505         goto usage;
01506       if (argv[i][1] == 'e')
01507         strcpy(exp_name, argv[++i]);
01508       else if (argv[i][1] == 'h')
01509         strcpy(host_name, argv[++i]);
01510       else
01511         {
01512 usage:
01513         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01514         return 1;
01515         }
01516       }
01517     }
01518   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01519   if (status != CM_SUCCESS)
01520     return 1;
01521     ...do anyting...
01522   cm_disconnect_experiment();
01523 }
01524 \endcode
01525 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
01526 @param host_name_size     string length
01527 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
01528 @param exp_name_size      string length
01529 @return CM_SUCCESS
01530 */
01531 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name,
01532                        int exp_name_size)
01533 {
01534    host_name[0] = exp_name[0] = 0;
01535 
01536    if (getenv("MIDAS_SERVER_HOST"))
01537       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
01538 
01539    if (getenv("MIDAS_EXPT_NAME"))
01540       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
01541 
01542    return CM_SUCCESS;
01543 }
01544 
01545 
01546 /**dox***************************************************************/
01547 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01548 
01549 /********************************************************************/
01550 void cm_check_connect(void)
01551 {
01552    if (_hKeyClient)
01553       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
01554 }
01555 
01556 /**dox***************************************************************/
01557 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01558 
01559 /********************************************************************/
01560 /**
01561 This function connects to an existing MIDAS experiment.
01562 This must be the first call in a MIDAS application.
01563 It opens three TCP connection to the remote host (one for RPC calls,
01564 one to send events and one for hot-link notifications from the remote host)
01565 and writes client information into the ODB under /System/Clients.
01566 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
01567 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
01568 experiment name (see @ref Environment_variables).
01569 For that purpose, the function cm_get_environment()
01570 should be called prior to cm_connect_experiment(). If command line
01571 parameters -h and -e are used, the evaluation should be done between
01572 cm_get_environment() and cm_connect_experiment(). The function
01573 cm_disconnect_experiment() must be called before a MIDAS application exits.
01574 \code
01575 #include <stdio.h>
01576 #include <midas.h>
01577 main(int argc, char *argv[])
01578 {
01579   INT  status, i;
01580   char host_name[256],exp_name[32];
01581 
01582   // get default values from environment
01583   cm_get_environment(host_name, exp_name);
01584 
01585   // parse command line parameters
01586   for (i=1 ; i<argc ; i++)
01587     {
01588     if (argv[i][0] == '-')
01589       {
01590       if (i+1 >= argc || argv[i+1][0] == '-')
01591         goto usage;
01592       if (argv[i][1] == 'e')
01593         strcpy(exp_name, argv[++i]);
01594       else if (argv[i][1] == 'h')
01595         strcpy(host_name, argv[++i]);
01596       else
01597         {
01598 usage:
01599         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01600         return 1;
01601         }
01602       }
01603     }
01604   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01605   if (status != CM_SUCCESS)
01606     return 1;
01607   ...do operations...
01608   cm_disconnect_experiment();
01609 }
01610 \endcode
01611 @param host_name Specifies host to connect to. Must be a valid IP host name.
01612   The string can be empty ("") if to connect to the local computer.
01613 @param exp_name Specifies the experiment to connect to.
01614   If this string is empty, the number of defined experiments in exptab is checked.
01615   If only one experiment is defined, the function automatically connects to this
01616   one. If more than one experiment is defined, a list is presented and the user
01617   can interactively select one experiment.
01618 @param client_name Client name of the calling program as it can be seen by
01619   others (like the scl command in ODBEdit).
01620 @param func Callback function to read in a password if security has
01621   been enabled. In all command line applications this function is NULL which
01622   invokes an internal ss_gets() function to read in a password.
01623   In windows environments (MS Windows, X Windows) a function can be supplied to
01624   open a dialog box and read in the password. The argument of this function must
01625   be the returned password.
01626 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br> 
01627 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
01628 */
01629 INT cm_connect_experiment(char *host_name, char *exp_name,
01630                           char *client_name, void (*func) (char *))
01631 {
01632    INT status;
01633    char str[256];
01634 
01635    status = cm_connect_experiment1(host_name, exp_name, client_name,
01636                                    func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
01637    if (status != CM_SUCCESS) {
01638       cm_get_error(status, str);
01639       puts(str);
01640    }
01641 
01642    return status;
01643 }
01644 
01645 /********************************************************************/
01646 /**
01647 Connect to a MIDAS experiment (to the online database) on
01648            a specific host.
01649 @internal
01650 */
01651 INT cm_connect_experiment1(char *host_name, char *exp_name,
01652                            char *client_name, void (*func) (char *),
01653                            INT odb_size, DWORD watchdog_timeout)
01654 {
01655    INT status, i, mutex_elog, mutex_alarm, size;
01656    char local_host_name[HOST_NAME_LENGTH];
01657    char client_name1[NAME_LENGTH];
01658    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
01659    HNDLE hDB, hKeyClient;
01660    BOOL call_watchdog;
01661    RUNINFO_STR(runinfo_str);
01662 
01663    if (_hKeyClient)
01664       cm_disconnect_experiment();
01665 
01666    rpc_set_name(client_name);
01667 
01668    /* check for local host */
01669    if (equal_ustring(host_name, "local"))
01670       host_name[0] = 0;
01671 
01672 #ifdef OS_WINNT
01673    {
01674       WSADATA WSAData;
01675 
01676       /* Start windows sockets */
01677       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01678          return RPC_NET_ERROR;
01679    }
01680 #endif
01681 
01682    /* search for experiment name in exptab */
01683    if (exp_name == NULL)
01684       exp_name = "";
01685 
01686    strcpy(exp_name1, exp_name);
01687    if (exp_name1[0] == 0) {
01688       status = cm_select_experiment(host_name, exp_name1);
01689       if (status != CM_SUCCESS)
01690          return status;
01691    }
01692 
01693    /* connect to MIDAS server */
01694    if (host_name[0]) {
01695       status = rpc_server_connect(host_name, exp_name1);
01696       if (status != RPC_SUCCESS)
01697          return status;
01698 
01699       /* register MIDAS library functions */
01700       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
01701       if (status != RPC_SUCCESS)
01702          return status;
01703    } else {
01704       /* lookup path for *SHM files and save it */
01705       status = cm_scan_experiments();
01706       if (status != CM_SUCCESS)
01707          return status;
01708 
01709       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
01710          if (equal_ustring(exp_name1, exptab[i].name))
01711             break;
01712 
01713       /* return if experiment not defined */
01714       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
01715          /* message should be displayed by application
01716             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
01717             cm_msg(MERROR, str);
01718           */
01719          return CM_UNDEF_EXP;
01720       }
01721 
01722       cm_set_path(exptab[i].directory);
01723 
01724       /* create alarm and elog mutexes */
01725       status = ss_mutex_create("ALARM", &mutex_alarm);
01726       if (status != SS_CREATED && status != SS_SUCCESS) {
01727          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
01728          return status;
01729       }
01730       status = ss_mutex_create("ELOG", &mutex_elog);
01731       if (status != SS_CREATED && status != SS_SUCCESS) {
01732          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
01733          return status;
01734       }
01735       cm_set_experiment_mutex(mutex_alarm, mutex_elog);
01736    }
01737 
01738    /* open ODB */
01739    if (odb_size == 0)
01740       odb_size = DEFAULT_ODB_SIZE;
01741 
01742    status = db_open_database("ODB", odb_size, &hDB, client_name);
01743    if (status != DB_SUCCESS && status != DB_CREATED) {
01744       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01745       return status;
01746    }
01747 
01748    /* now setup client info */
01749    gethostname(local_host_name, sizeof(local_host_name));
01750 
01751    /* check watchdog timeout */
01752    if (watchdog_timeout == 0)
01753       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01754 
01755    strcpy(client_name1, client_name);
01756    password[0] = 0;
01757    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01758                                client_name1, rpc_get_option(0, RPC_OHW_TYPE),
01759                                password, watchdog_timeout);
01760 
01761    if (status == CM_WRONG_PASSWORD) {
01762       if (func == NULL)
01763          strcpy(str, ss_getpass("Password: "));
01764       else
01765          func(str);
01766 
01767       /* re-open database */
01768       status = db_open_database("ODB", odb_size, &hDB, client_name);
01769       if (status != DB_SUCCESS && status != DB_CREATED) {
01770          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01771          return status;
01772       }
01773 
01774       strcpy(password, ss_crypt(str, "mi"));
01775       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01776                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE),
01777                                   password, watchdog_timeout);
01778       if (status != CM_SUCCESS) {
01779          /* disconnect */
01780          if (rpc_is_remote())
01781             rpc_server_disconnect();
01782 
01783          return status;
01784       }
01785    }
01786 
01787    cm_set_experiment_database(hDB, hKeyClient);
01788 
01789    /* set experiment name in ODB */
01790    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
01791 
01792    /* set data dir in ODB */
01793    cm_get_path(str);
01794    size = sizeof(str);
01795    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
01796 
01797    /* check /runinfo structure */
01798    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), TRUE);
01799    if (status == DB_STRUCT_MISMATCH) {
01800       cm_msg(MERROR, "cm_connect_experiment1",
01801              "Aborting on mismatching /Runinfo structure");
01802       cm_disconnect_experiment();
01803       abort();
01804    }
01805 
01806    /* register server to be able to be called by other clients */
01807    status = cm_register_server();
01808    if (status != CM_SUCCESS)
01809       return status;
01810 
01811    /* set watchdog timeout */
01812    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
01813    size = sizeof(watchdog_timeout);
01814    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
01815    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01816    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01817 
01818    /* send startup notification */
01819    if (strchr(local_host_name, '.'))
01820       *strchr(local_host_name, '.') = 0;
01821 
01822    /* startup message is not displayed */
01823    _message_print = NULL;
01824 
01825    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started",
01826           client_name, local_host_name);
01827 
01828    /* enable system and user messages to stdout as default */
01829    cm_set_msg_print(MT_ALL, MT_ALL, puts);
01830 
01831    /* call cm_check_connect when exiting */
01832    atexit((void (*)(void)) cm_check_connect);
01833 
01834    /* register ctrl-c handler */
01835    ss_ctrlc_handler(cm_ctrlc_handler);
01836 
01837    return CM_SUCCESS;
01838 }
01839 
01840 /********************************************************************/
01841 /** 
01842 Connect to a MIDAS server and return all defined
01843            experiments in *exp_name[MAX_EXPERIMENTS]
01844 @param  host_name         Internet host name.
01845 @param  exp_name          list of experiment names
01846 @return CM_SUCCESS, RPC_NET_ERROR
01847 */
01848 INT cm_list_experiments(char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
01849 {
01850    INT i, status;
01851    struct sockaddr_in bind_addr;
01852    INT sock;
01853    char str[MAX_EXPERIMENT * NAME_LENGTH];
01854    struct hostent *phe;
01855 
01856    if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
01857       status = cm_scan_experiments();
01858       if (status != CM_SUCCESS)
01859          return status;
01860 
01861       for (i = 0; i < MAX_EXPERIMENT; i++)
01862          strcpy(exp_name[i], exptab[i].name);
01863 
01864       return CM_SUCCESS;
01865    }
01866 #ifdef OS_WINNT
01867    {
01868       WSADATA WSAData;
01869 
01870       /* Start windows sockets */
01871       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01872          return RPC_NET_ERROR;
01873    }
01874 #endif
01875 
01876    /* create a new socket for connecting to remote server */
01877    sock = socket(AF_INET, SOCK_STREAM, 0);
01878    if (sock == -1) {
01879       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
01880       return RPC_NET_ERROR;
01881    }
01882 
01883    /* connect to remote node */
01884    memset(&bind_addr, 0, sizeof(bind_addr));
01885    bind_addr.sin_family = AF_INET;
01886    bind_addr.sin_addr.s_addr = 0;
01887    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
01888 
01889 #ifdef OS_VXWORKS
01890    {
01891       INT host_addr;
01892 
01893       host_addr = hostGetByName(host_name);
01894       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
01895    }
01896 #else
01897    phe = gethostbyname(host_name);
01898    if (phe == NULL) {
01899       cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
01900       return RPC_NET_ERROR;
01901    }
01902    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
01903 #endif
01904 
01905 #ifdef OS_UNIX
01906    do {
01907       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
01908 
01909       /* don't return if an alarm signal was cought */
01910    } while (status == -1 && errno == EINTR);
01911 #else
01912    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
01913 #endif
01914 
01915    if (status != 0) {
01916 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
01917       return RPC_NET_ERROR;
01918    }
01919 
01920    /* request experiment list */
01921    send(sock, "I", 2, 0);
01922 
01923    for (i = 0; i < MAX_EXPERIMENT; i++) {
01924       exp_name[i][0] = 0;
01925       status = recv_string(sock, str, sizeof(str), 1000);
01926 
01927       if (status < 0)
01928          return RPC_NET_ERROR;
01929 
01930       if (status == 0)
01931          break;
01932 
01933       strcpy(exp_name[i], str);
01934    }
01935 
01936    exp_name[i][0] = 0;
01937    closesocket(sock);
01938 
01939    return CM_SUCCESS;
01940 }
01941 
01942 /********************************************************************/
01943 /**
01944 Connect to a MIDAS server and select an experiment
01945            from the experiments available on this server
01946 @internal
01947 @param  host_name         Internet host name.
01948 @param  exp_name          list of experiment names
01949 @return CM_SUCCESS, RPC_NET_ERROR
01950 */
01951 INT cm_select_experiment(char *host_name, char *exp_name)
01952 {
01953    INT status, i;
01954    char expts[MAX_EXPERIMENT][NAME_LENGTH];
01955    char str[32];
01956 
01957    /* retrieve list of experiments and make selection */
01958    status = cm_list_experiments(host_name, expts);
01959    if (status != CM_SUCCESS)
01960       return status;
01961 
01962    if (expts[1][0]) {
01963       if (host_name[0])
01964          printf("Available experiments on server %s:\n", host_name);
01965       else
01966          printf("Available experiments on local computer:\n");
01967 
01968       for (i = 0; expts[i][0]; i++)
01969          printf("%d : %s\n", i, expts[i]);
01970       printf("Select number: ");
01971       ss_gets(str, 32);
01972       i = atoi(str);
01973       strcpy(exp_name, expts[i]);
01974    } else
01975       strcpy(exp_name, expts[0]);
01976 
01977    return CM_SUCCESS;
01978 }
01979 
01980 /********************************************************************/
01981 /**
01982 Connect to a MIDAS client of the current experiment
01983 @internal
01984 @param  client_name       Name of client to connect to. This name
01985                             is set by the other client via the
01986                             cm_connect_experiment call.
01987 @param  hConn            Connection handle
01988 @return CM_SUCCESS, CM_NO_CLIENT
01989 */
01990 INT cm_connect_client(char *client_name, HNDLE * hConn)
01991 {
01992    HNDLE hDB, hKeyRoot, hSubkey, hKey;
01993    INT status, i, length, port;
01994    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
01995 
01996    /* find client entry in ODB */
01997    cm_get_experiment_database(&hDB, &hKey);
01998 
01999    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02000    if (status != DB_SUCCESS)
02001       return status;
02002 
02003    i = 0;
02004    do {
02005       /* search for client with specific name */
02006       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02007       if (status == DB_NO_MORE_SUBKEYS)
02008          return CM_NO_CLIENT;
02009 
02010       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02011       if (status != DB_SUCCESS)
02012          return status;
02013 
02014       length = NAME_LENGTH;
02015       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02016       if (status != DB_SUCCESS)
02017          return status;
02018 
02019       if (equal_ustring(name, client_name)) {
02020          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02021          if (status != DB_SUCCESS)
02022             return status;
02023 
02024          length = sizeof(INT);
02025          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02026          if (status != DB_SUCCESS)
02027             return status;
02028 
02029          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02030          if (status != DB_SUCCESS)
02031             return status;
02032 
02033          length = sizeof(host_name);
02034          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02035          if (status != DB_SUCCESS)
02036             return status;
02037 
02038          /* client found -> connect to its server port */
02039          return rpc_client_connect(host_name, port, client_name, hConn);
02040       }
02041 
02042 
02043    } while (TRUE);
02044 }
02045 
02046 /********************************************************************/
02047 /**
02048 Disconnect from a MIDAS client 
02049 @param   hConn             Connection handle obtained via
02050                              cm_connect_client()
02051 @param   bShutdown         If TRUE, disconnect from client and
02052                              shut it down (exit the client program)
02053                              by sending a RPC_SHUTDOWN message
02054 @return   see rpc_client_disconnect()
02055 */
02056 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02057 {
02058    return rpc_client_disconnect(hConn, bShutdown);
02059 }
02060 
02061 /********************************************************************/
02062 /**
02063 Disconnect from a MIDAS experiment.
02064 @attention Should be the last call to a MIDAS library function in an
02065 application before it exits. This function removes the client information
02066 from the ODB, disconnects all TCP connections and frees all internal
02067 allocated memory. See cm_connect_experiment() for example.
02068 @return CM_SUCCESS
02069 */
02070 INT cm_disconnect_experiment(void)
02071 {
02072    HNDLE hDB, hKey;
02073    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02074 
02075    /* send shutdown notification */
02076    rpc_get_name(client_name);
02077    gethostname(local_host_name, sizeof(local_host_name));
02078    if (strchr(local_host_name, '.'))
02079       *strchr(local_host_name, '.') = 0;
02080 
02081    /* disconnect message not displayed */
02082    _message_print = NULL;
02083 
02084    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped",
02085           client_name, local_host_name);
02086 
02087    if (rpc_is_remote()) {
02088       /* close open records */
02089       db_close_all_records();
02090 
02091       rpc_client_disconnect(-1, FALSE);
02092       rpc_server_disconnect();
02093    } else {
02094       rpc_client_disconnect(-1, FALSE);
02095 
02096 #ifdef LOCAL_ROUTINES
02097       ss_alarm(0, cm_watchdog);
02098       _watchdog_last_called = 0;
02099 #endif                          /* LOCAL_ROUTINES */
02100 
02101       /* delete client info */
02102       cm_get_experiment_database(&hDB, &hKey);
02103 
02104       if (hDB)
02105          cm_delete_client_info(hDB, 0);
02106 
02107       bm_close_all_buffers();
02108       db_close_all_databases();
02109    }
02110 
02111    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02112       rpc_server_shutdown();
02113 
02114    /* free RPC list */
02115    rpc_deregister_functions();
02116 
02117    cm_set_experiment_database(0, 0);
02118 
02119    _msg_buffer = 0;
02120 
02121    /* free memory buffers */
02122    if (_event_buffer_size > 0) {
02123       M_FREE(_event_buffer);
02124       _event_buffer_size = 0;
02125    }
02126 
02127    if (_net_recv_buffer_size > 0) {
02128       M_FREE(_net_recv_buffer);
02129       _net_recv_buffer_size = 0;
02130    }
02131 
02132    if (_net_send_buffer_size > 0) {
02133       M_FREE(_net_send_buffer);
02134       _net_send_buffer_size = 0;
02135    }
02136 
02137    if (_tcp_buffer != NULL) {
02138       M_FREE(_tcp_buffer);
02139       _tcp_buffer = NULL;
02140    }
02141 
02142    return CM_SUCCESS;
02143 }
02144 
02145 /********************************************************************/
02146 /**
02147 Set the handle to the ODB for the currently connected experiment
02148 @param hDB              Database handle
02149 @param hKeyClient       Key handle of client structure
02150 @return CM_SUCCESS
02151 */
02152 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02153 {
02154    _hDB = hDB;
02155    _hKeyClient = hKeyClient;
02156 
02157    return CM_SUCCESS;
02158 }
02159 
02160 
02161 
02162 /**dox***************************************************************/
02163 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02164 
02165 /********************************************************************/
02166 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog)
02167 /********************************************************************\
02168 
02169   Routine: cm_set_experiment_mutex
02170 
02171   Purpose: Set the handle to the experiment wide mutexes
02172 
02173   Input:
02174     INT    mutex_alarm      Alarm mutex
02175     INT    mutex_elog       Elog mutex
02176 
02177   Output:
02178     none
02179 
02180   Function value:
02181     CM_SUCCESS              Successful completion
02182 
02183 \********************************************************************/
02184 {
02185    _mutex_alarm = mutex_alarm;
02186    _mutex_elog = mutex_elog;
02187 
02188    return CM_SUCCESS;
02189 }
02190 
02191 /**dox***************************************************************/
02192 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02193 
02194 /********************************************************************/
02195 /** 
02196 Get the handle to the ODB from the currently connected experiment.
02197 
02198 @attention This function returns the handle of the online database (ODB) which
02199 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02200 to access the client information in the ODB. If the client key handle is not needed,
02201 the parameter can be NULL.
02202 \code
02203 HNDLE hDB, hkeyclient;
02204  char  name[32];
02205  int   size;
02206  db_get_experiment_database(&hdb, &hkeyclient);
02207  size = sizeof(name);
02208  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02209  printf("My name is %s\n", name);
02210 \endcode
02211 @param hDB Database handle.
02212 @param hKeyClient Handle for key where search starts, zero for root.
02213 @return CM_SUCCESS
02214 */
02215 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02216 {
02217    if (_hDB) {
02218       if (hDB != NULL)
02219          *hDB = _hDB;
02220       if (hKeyClient != NULL)
02221          *hKeyClient = _hKeyClient;
02222    } else {
02223       if (hDB != NULL)
02224          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02225       if (hKeyClient != NULL)
02226          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02227    }
02228 
02229    return CM_SUCCESS;
02230 }
02231 
02232 /**dox***************************************************************/
02233 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02234 
02235 /********************************************************************/
02236 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog)
02237 /********************************************************************\
02238 
02239   Routine: cm_get_experiment_mutex
02240 
02241   Purpose: Get the handle to the experiment wide mutexes
02242 
02243   Input:
02244     none
02245 
02246   Output:
02247     INT    mutex_alarm      Alarm mutex
02248     INT    mutex_elog       Elog mutex
02249 
02250   Function value:
02251     CM_SUCCESS              Successful completion
02252 
02253 \********************************************************************/
02254 {
02255    if (mutex_alarm)
02256       *mutex_alarm = _mutex_alarm;
02257    if (mutex_elog)
02258       *mutex_elog = _mutex_elog;
02259 
02260    return CM_SUCCESS;
02261 }
02262 
02263 /**dox***************************************************************/
02264 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02265 
02266 /********************************************************************/
02267 /**
02268 Sets the internal watchdog flags and the own timeout.
02269 If call_watchdog is TRUE, the cm_watchdog routine is called
02270 periodically from the system to show other clients that
02271 this application is "alive". On UNIX systems, the
02272 alarm() timer is used which is then not available for
02273 user purposes.
02274 
02275 The timeout specifies the time, after which the calling
02276 application should be considered "dead" by other clients.
02277 Normally, the cm_watchdog() routines is called periodically.
02278 If a client crashes, this does not occur any more. Then
02279 other clients can detect this and clear all buffer and
02280 database entries of this application so they are not
02281 blocked any more. If this application should not checked
02282 by others, the timeout can be specified as zero.
02283 It might be useful for debugging purposes to do so,
02284 because if a debugger comes to a breakpoint and stops
02285 the application, the periodic call of cm_watchdog
02286 is disabled and the client looks like dead.
02287 
02288 If the timeout is not zero, but the watchdog is not
02289 called (call_watchdog == FALSE), the user must ensure
02290 to call cm_watchdog periodically with a period of
02291 WATCHDOG_INTERVAL milliseconds or less.
02292 
02293 An application which calles system routines which block
02294 the alarm signal for some time, might increase the
02295 timeout to the maximum expected blocking time before
02296 issuing the calls. One example is the logger doing
02297 Exabyte tape IO, which can take up to one minute.
02298 @param    call_watchdog   Call the cm_watchdog routine periodically
02299 @param    timeout         Timeout for this application in ms
02300 @return   CM_SUCCESS
02301 */
02302 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
02303 {
02304    INT i;
02305 
02306    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
02307    _watchdog_timeout = timeout;
02308 
02309    if (rpc_is_remote())
02310       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
02311 
02312 #ifdef LOCAL_ROUTINES
02313 
02314    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
02315       HNDLE hDB, hKey;
02316 
02317       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
02318 
02319       /* write timeout value to client enty in ODB */
02320       cm_get_experiment_database(&hDB, &hKey);
02321 
02322       if (hDB) {
02323          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02324          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
02325          db_set_mode(hDB, hKey, MODE_READ, TRUE);
02326       }
02327    } else {
02328       _call_watchdog = call_watchdog;
02329       _watchdog_timeout = timeout;
02330 
02331       /* set watchdog flag of all open buffers */
02332       for (i = _buffer_entries; i > 0; i--) {
02333          BUFFER_CLIENT *pclient;
02334          BUFFER_HEADER *pheader;
02335          INT index;
02336 
02337          index = _buffer[i - 1].client_index;
02338          pheader = _buffer[i - 1].buffer_header;
02339          pclient = &pheader->client[index];
02340 
02341          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02342              _buffer[i - 1].index != rpc_get_server_acception())
02343             continue;
02344 
02345          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
02346              _buffer[i - 1].index != ss_gettid())
02347             continue;
02348 
02349          if (!_buffer[i - 1].attached)
02350             continue;
02351 
02352          /* clear entry from client structure in buffer header */
02353          pclient->watchdog_timeout = timeout;
02354 
02355          /* show activity */
02356          pclient->last_activity = ss_millitime();
02357       }
02358 
02359       /* set watchdog flag of alll open databases */
02360       for (i = _database_entries; i > 0; i--) {
02361          DATABASE_HEADER *pheader;
02362          DATABASE_CLIENT *pclient;
02363          INT index;
02364 
02365          db_lock_database(i);
02366          index = _database[i - 1].client_index;
02367          pheader = _database[i - 1].database_header;
02368          pclient = &pheader->client[index];
02369 
02370          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02371              _database[i - 1].index != rpc_get_server_acception()) {
02372             db_unlock_database(i);
02373             continue;
02374          }
02375 
02376          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
02377              _database[i - 1].index != ss_gettid()) {
02378             db_unlock_database(i);
02379             continue;
02380          }
02381 
02382          if (!_database[i - 1].attached) {
02383             db_unlock_database(i);
02384             continue;
02385          }
02386 
02387          /* clear entry from client structure in buffer header */
02388          pclient->watchdog_timeout = timeout;
02389 
02390          /* show activity */
02391          pclient->last_activity = ss_millitime();
02392 
02393          db_unlock_database(i);
02394       }
02395 
02396       if (call_watchdog)
02397          /* restart watchdog */
02398          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02399       else
02400          /* kill current timer */
02401          ss_alarm(0, cm_watchdog);
02402    }
02403 
02404 #endif                          /* LOCAL_ROUTINES */
02405 
02406    return CM_SUCCESS;
02407 }
02408 
02409 /********************************************************************/
02410 /**
02411 Return the current watchdog parameters
02412 @param call_watchdog   Call the cm_watchdog routine periodically
02413 @param timeout         Timeout for this application in seconds
02414 @return   CM_SUCCESS
02415 */
02416 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
02417 {
02418    if (call_watchdog)
02419       *call_watchdog = _call_watchdog;
02420    if (timeout)
02421       *timeout = _watchdog_timeout;
02422 
02423    return CM_SUCCESS;
02424 }
02425 
02426 /********************************************************************/
02427 /**
02428 Return watchdog information about specific client
02429 @param    hDB              ODB handle
02430 @param    client_name     ODB client name
02431 @param    timeout         Timeout for this application in seconds
02432 @param    last            Last time watchdog was called in msec
02433 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE 
02434 */
02435 
02436 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
02437 {
02438    if (rpc_is_remote())
02439       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
02440 
02441 #ifdef LOCAL_ROUTINES
02442    {
02443       DATABASE_HEADER *pheader;
02444       DATABASE_CLIENT *pclient;
02445       INT i;
02446 
02447       if (hDB > _database_entries || hDB <= 0) {
02448          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02449          return DB_INVALID_HANDLE;
02450       }
02451 
02452       if (!_database[hDB - 1].attached) {
02453          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02454          return DB_INVALID_HANDLE;
02455       }
02456 
02457       /* lock database */
02458       db_lock_database(hDB);
02459 
02460       pheader = _database[hDB - 1].database_header;
02461       pclient = pheader->client;
02462 
02463       /* find client */
02464       for (i = 0; i < pheader->max_client_index; i++, pclient++)
02465          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
02466             *timeout = pclient->watchdog_timeout;
02467             *last = ss_millitime() - pclient->last_activity;
02468             db_unlock_database(hDB);
02469             return CM_SUCCESS;
02470          }
02471 
02472       *timeout = *last = 0;
02473 
02474       db_unlock_database(hDB);
02475 
02476       return CM_NO_CLIENT;
02477    }
02478 #else                           /* LOCAL_ROUTINES */
02479    return CM_SUCCESS;
02480 #endif                          /* LOCAL_ROUTINES */
02481 }
02482 
02483 
02484 /**dox***************************************************************/
02485 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02486 
02487 /********************************************************************/
02488 INT cm_register_server(void)
02489 /********************************************************************\
02490 
02491   Routine: cm_register_server
02492 
02493   Purpose: Register a server which can be called from other clients
02494            of a specific experiment.
02495 
02496   Input:
02497     none
02498 
02499   Output:
02500     none
02501 
02502   Function value:
02503     CM_SUCCESS              Successful completion
02504 
02505 \********************************************************************/
02506 {
02507    INT status, port;
02508    HNDLE hDB, hKey;
02509 
02510    if (!_server_registered) {
02511       port = 0;
02512       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
02513       if (status != RPC_SUCCESS)
02514          return status;
02515       _server_registered = TRUE;
02516 
02517       /* register MIDAS library functions */
02518       rpc_register_functions(rpc_get_internal_list(1), NULL);
02519 
02520       /* store port number in ODB */
02521       cm_get_experiment_database(&hDB, &hKey);
02522 
02523       status = db_find_key(hDB, hKey, "Server Port", &hKey);
02524       if (status != DB_SUCCESS)
02525          return status;
02526 
02527       /* unlock database */
02528       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02529 
02530       /* set value */
02531       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
02532       if (status != DB_SUCCESS)
02533          return status;
02534 
02535       /* lock database */
02536       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02537    }
02538 
02539    return CM_SUCCESS;
02540 }
02541 
02542 /**dox***************************************************************/
02543 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02544 
02545 /********************************************************************/
02546 /**
02547 Registers a callback function for run transitions.
02548 This function internally registers the transition callback
02549 function and publishes its request for transition notification by writing
02550 a transition request to /System/Clients/<pid>/Transition XXX.
02551 Other clients making a transition scan the transition requests of all clients
02552 and call their transition callbacks via RPC.
02553 
02554 Clients can register for transitions (Start/Stop/Pause/Resume) in a given
02555 sequence. All sequence numbers given in the registration are sorted on 
02556 a transition and the clients are contacted in ascending order. By default,
02557 all programs register with a sequence number of 500. The logger however
02558 uses 200 for start, so that it can open files before the other clients
02559 are contacted, and 800 for stop, so that the files get closed when all
02560 other clients have gone already through the stop trantition.
02561 
02562 The callback function returns CM_SUCCESS if it can perform the transition or
02563 a value larger than one in case of error. An error string can be copied
02564 into the error variable.
02565 @attention The callback function will be called on transitions from inside the
02566     cm_yield() function which therefore must be contained in the main program loop.
02567 \code
02568 INT start(INT run_number, char *error)
02569 {
02570   if (<not ok>)
02571     {
02572     strcpy(error, "Cannot start because ...");
02573     return 2;
02574     }
02575   printf("Starting run %d\n", run_number);
02576   return CM_SUCCESS;
02577 }
02578 main()
02579 {
02580   ...
02581   cm_register_transition(TR_START, start, 500);
02582   do
02583     {
02584     status = cm_yield(1000);
02585     } while (status != RPC_SHUTDOWN &&
02586              status != SS_ABORT);
02587   ...
02588 }
02589 \endcode
02590 @param transition Transition to register for (see @ref state_transition)
02591 @param func Callback function.
02592 @param sequence_number Sequence number for that transition (1..1000)
02593 @return CM_SUCCESS
02594 */
02595 INT cm_register_transition(INT transition, INT(*func) (INT, char *), INT sequence_number)
02596 {
02597    INT status, i;
02598    HNDLE hDB, hKey, hKeyTrans;
02599    KEY key;
02600    char str[256];
02601 
02602    /* check for valid transition */
02603    if (transition != TR_START && transition != TR_STOP &&
02604        transition != TR_PAUSE && transition != TR_RESUME) {
02605       cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"",
02606              transition);
02607       return CM_INVALID_TRANSITION;
02608    }
02609 
02610    cm_get_experiment_database(&hDB, &hKey);
02611 
02612    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
02613 
02614    /* register new transition request */
02615 
02616    /* find empty slot */
02617    for (i = 0; i < MAX_TRANSITIONS; i++)
02618       if (!_trans_table[i].transition)
02619          break;
02620 
02621    if (i == MAX_TRANSITIONS) {
02622       cm_msg(MERROR, "cm_register_transition",
02623              "To many transition registrations. Please increase MAX_TRANSITIONS and recompile");
02624       return CM_TOO_MANY_REQUESTS;
02625    }
02626 
02627    _trans_table[i].transition = transition;
02628    _trans_table[i].func = func;
02629    _trans_table[i].sequence_number = sequence_number;
02630 
02631    for (i = 0; i < 13; i++)
02632       if (trans_name[i].transition == transition)
02633          break;
02634 
02635    sprintf(str, "Transition %s", trans_name[i].name);
02636 
02637    /* unlock database */
02638    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02639 
02640    /* set value */
02641    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02642    if (!hKeyTrans) {
02643       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02644       if (status != DB_SUCCESS)
02645          return status;
02646    } else {
02647       status = db_get_key(hDB, hKeyTrans, &key);
02648       if (status != DB_SUCCESS)
02649          return status;
02650       status =
02651           db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values,
02652                             TID_INT);
02653       if (status != DB_SUCCESS)
02654          return status;
02655    }
02656 
02657    /* re-lock database */
02658    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02659 
02660    return CM_SUCCESS;
02661 }
02662 
02663 INT cm_deregister_transition(INT transition)
02664 {
02665    INT status, i;
02666    HNDLE hDB, hKey, hKeyTrans;
02667    char str[256];
02668 
02669    /* check for valid transition */
02670    if (transition != TR_START && transition != TR_STOP &&
02671        transition != TR_PAUSE && transition != TR_RESUME) {
02672       cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"",
02673              transition);
02674       return CM_INVALID_TRANSITION;
02675    }
02676 
02677    cm_get_experiment_database(&hDB, &hKey);
02678 
02679    /* remove existing transition request */
02680    for (i = 0; i < MAX_TRANSITIONS; i++)
02681       if (_trans_table[i].transition == transition)
02682          break;
02683 
02684    if (i == MAX_TRANSITIONS) {
02685       cm_msg(MERROR, "cm_register_transition",
02686              "Cannot de-register transition registration, request not found");
02687       return CM_INVALID_TRANSITION;
02688    }
02689 
02690    _trans_table[i].transition = 0;
02691    _trans_table[i].func = NULL;
02692    _trans_table[i].sequence_number = 0;
02693 
02694    for (i = 0; i < 13; i++)
02695       if (trans_name[i].transition == transition)
02696          break;
02697 
02698    sprintf(str, "Transition %s", trans_name[i].name);
02699 
02700    /* unlock database */
02701    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02702 
02703    /* set value */
02704    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02705    if (hKeyTrans) {
02706       status = db_delete_key(hDB, hKeyTrans, FALSE);
02707       if (status != DB_SUCCESS)
02708          return status;
02709    }
02710 
02711    /* re-lock database */
02712    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02713 
02714    return CM_SUCCESS;
02715 }
02716 
02717 /********************************************************************/
02718 /**
02719 Change the transition sequence for the calling program.
02720 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02721 @param sequence_number New sequence number, should be between 1 and 1000
02722 @return     CM_SUCCESS
02723 */
02724 INT cm_set_transition_sequence(INT transition, INT sequence_number)
02725 {
02726    INT status, i;
02727    HNDLE hDB, hKey;
02728    char str[256];
02729 
02730    /* check for valid transition */
02731    if (transition != TR_START && transition != TR_STOP &&
02732        transition != TR_PAUSE && transition != TR_RESUME) {
02733       cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"",
02734              transition);
02735       return CM_INVALID_TRANSITION;
02736    }
02737 
02738    cm_get_experiment_database(&hDB, &hKey);
02739 
02740    /* Find the transition type from the list */
02741    for (i = 0; i < 13; i++)
02742       if (trans_name[i].transition == transition)
02743          break;
02744    sprintf(str, "Transition %s", trans_name[i].name);
02745 
02746    /* Change local sequence number for this transition type */
02747    for (i = 0; i < MAX_TRANSITIONS; i++)
02748       if (_trans_table[i].transition == transition) {
02749          _trans_table[i].sequence_number = sequence_number;
02750          break;
02751       }
02752 
02753    /* unlock database */
02754    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02755 
02756    /* set value */
02757    status = db_set_value(hDB, hKey, str, &sequence_number, 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    return CM_SUCCESS;
02765 
02766 }
02767 
02768 /**dox***************************************************************/
02769 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02770 
02771 static INT _requested_transition;
02772 static DWORD _deferred_transition_mask;
02773 
02774 /**dox***************************************************************/
02775 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02776 
02777 /********************************************************************/
02778 /**
02779 Register a deferred transition handler. If a client is
02780 registered as a deferred transition handler, it may defer
02781 a requested transition by returning FALSE until a certain
02782 condition (like a motor reaches its end position) is
02783 reached.
02784 @param transition      One of TR_xxx
02785 @param (*func)         Function which gets called whenever
02786                        a transition is requested. If it returns
02787                        FALSE, the transition is not performed.
02788 @return CM_SUCCESS,    <error> Error from ODB access
02789 */
02790 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
02791 {
02792    INT status, i, size;
02793    char tr_key_name[256];
02794    HNDLE hDB, hKey;
02795 
02796    cm_get_experiment_database(&hDB, &hKey);
02797 
02798    for (i = 0; _deferred_trans_table[i].transition; i++)
02799       if (_deferred_trans_table[i].transition == transition)
02800          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
02801 
02802    /* set new transition mask */
02803    _deferred_transition_mask |= transition;
02804 
02805    for (i = 0; i < 13; i++)
02806       if (trans_name[i].transition == transition)
02807          break;
02808 
02809    sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
02810 
02811    /* unlock database */
02812    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02813 
02814    /* set value */
02815    i = 0;
02816    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT);
02817    if (status != DB_SUCCESS)
02818       return status;
02819 
02820    /* re-lock database */
02821    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02822 
02823    /* hot link requested transition */
02824    size = sizeof(_requested_transition);
02825    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size,
02826                 TID_INT, TRUE);
02827    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
02828    status =
02829        db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL,
02830                       NULL);
02831    if (status != DB_SUCCESS) {
02832       cm_msg(MERROR, "cm_register_deferred_transition",
02833              "Cannot hotlink /Runinfo/Requested Transition");
02834       return status;
02835    }
02836 
02837    return CM_SUCCESS;
02838 }
02839 
02840 /********************************************************************/
02841 /**
02842 Check for any deferred transition. If a deferred transition
02843 handler has been registered via the
02844 cm_register_deferred_transition function, this routine
02845 should be called regularly. It checks if a transition
02846 request is pending. If so, it calld the registered handler
02847 if the transition should be done and then actually does
02848 the transition.
02849 @return     CM_SUCCESS, <error>  Error from cm_transition()
02850 */
02851 INT cm_check_deferred_transition()
02852 {
02853    INT i, status;
02854    char str[256];
02855    static BOOL first;
02856 
02857    if (_requested_transition == 0)
02858       first = TRUE;
02859 
02860    if (_requested_transition & _deferred_transition_mask) {
02861       for (i = 0; _deferred_trans_table[i].transition; i++)
02862          if (_deferred_trans_table[i].transition == _requested_transition)
02863             break;
02864 
02865       if (_deferred_trans_table[i].transition == _requested_transition) {
02866          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition,
02867                                                                    first)) {
02868             status =
02869                 cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str),
02870                               SYNC, FALSE);
02871             if (status != CM_SUCCESS)
02872                cm_msg(MERROR, "cm_check_deferred_transition",
02873                       "Cannot perform deferred transition: %s", str);
02874 
02875             /* bypass hotlink and set _requested_transition directly to zero */
02876             _requested_transition = 0;
02877 
02878             return status;
02879          }
02880          first = FALSE;
02881       }
02882    }
02883 
02884    return SUCCESS;
02885 }
02886 
02887 
02888 /**dox***************************************************************/
02889 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02890 
02891 /********************************************************************/
02892 
02893 /**dox***************************************************************/
02894 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02895 
02896 typedef struct {
02897    int sequence_number;
02898    char host_name[HOST_NAME_LENGTH];
02899    char client_name[NAME_LENGTH];
02900    int port;
02901 } TR_CLIENT;
02902 
02903 int tr_compare(const void *arg1, const void *arg2)
02904 {
02905    return ((TR_CLIENT *) arg1)->sequence_number - ((TR_CLIENT *) arg2)->sequence_number;
02906 }
02907 
02908 /********************************************************************/
02909 /**
02910 Performs a run transition (Start/Stop/Pause/Resume).
02911 
02912 Synchronous/Asynchronous flag.
02913 If set to ASYNC, the transition is done
02914 asynchronously, meaning that clients are connected and told to execute their
02915 callback routine, but no result is awaited. The return value is
02916 specified by the transition callback function on the remote clients. If all callbacks
02917 can perform the transition, CM_SUCCESS is returned. If one callback cannot
02918 perform the transition, the return value of this callback is returned from
02919 cm_transition().
02920 The async_flag is usually FALSE so that transition callbacks can block a
02921 run transition in case of problems and return an error string. The only exception are
02922 situations where a run transition is performed automatically by a program which
02923 cannot block in a transition. For example the logger can cause a run stop when a
02924 disk is nearly full but it cannot block in the cm_transition() function since it
02925 has its own run stop callback which must flush buffers and close disk files and
02926 tapes.
02927 \code
02928 ...
02929     i = 1;
02930     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
02931 
02932       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
02933       if (status != CM_SUCCESS)
02934       {
02935         // in case of error
02936         printf("Error: %s\n", str);
02937       }
02938     ...
02939 \endcode
02940 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02941 @param run_number New run number. If zero, use current run number plus one.
02942 @param perror returned error string.
02943 @param strsize Size of error string.
02944 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
02945 @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
02946 @return CM_SUCCESS, <error> error code from remote client
02947 */
02948 INT cm_transition(INT transition, INT run_number, char *perror, INT strsize,
02949                   INT async_flag, INT debug_flag)
02950 {
02951    INT i, j, status, index, size, sequence_number, port, state, old_timeout, n_tr_clients;
02952    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hConn, hKeyTrans;
02953    DWORD seconds;
02954    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH],
02955        str[256], error[256], tr_key_name[256];
02956    char *trname = "unknown";
02957    KEY key;
02958    BOOL deferred;
02959    PROGRAM_INFO program_info;
02960    TR_CLIENT *tr_client;
02961 
02962    deferred = (transition & TR_DEFERRED) > 0;
02963    transition &= ~TR_DEFERRED;
02964 
02965    /* check for valid transition */
02966    if (transition != TR_START && transition != TR_STOP &&
02967        transition != TR_PAUSE && transition != TR_RESUME) {
02968       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
02969       return CM_INVALID_TRANSITION;
02970    }
02971 
02972    /* get key of local client */
02973    cm_get_experiment_database(&hDB, &hKeylocal);
02974 
02975    if (perror != NULL)
02976       strcpy(perror, "Success");
02977 
02978    /* if no run number is given, get it from DB */
02979    if (run_number == 0) {
02980       size = sizeof(run_number);
02981       status =
02982           db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
02983       assert(status == SUCCESS);
02984    }
02985 
02986    if (run_number <= 0) {
02987       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d",
02988              run_number);
02989       abort();
02990    }
02991 
02992    /* Set new run number in ODB */
02993    if (transition == TR_START) {
02994       if (debug_flag == 1)
02995          printf("Setting run number %d in ODB\n", run_number);
02996       if (debug_flag == 2)
02997          cm_msg(MDEBUG, "cm_transition", "cm_transition: Setting run number %d in ODB",
02998                 run_number);
02999 
03000       status = db_set_value(hDB, 0, "Runinfo/Run number",
03001                             &run_number, sizeof(run_number), 1, TID_INT);
03002       assert(status == SUCCESS);
03003       if (status != DB_SUCCESS)
03004          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
03005    }
03006 
03007    if (deferred) {
03008       /* remove transition request */
03009       i = 0;
03010       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
03011    } else {
03012       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03013       if (status != DB_SUCCESS) {
03014          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03015          return status;
03016       }
03017 
03018       /* check if deferred transition already in progress */
03019       size = sizeof(INT);
03020       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
03021       if (i) {
03022          if (perror)
03023             sprintf(perror, "Deferred transition already in progress");
03024 
03025          return CM_TRANSITION_IN_PROGRESS;
03026       }
03027 
03028       for (i = 0; trans_name[i].name[0] != 0; i++)
03029          if (trans_name[i].transition == transition) {
03030             trname = trans_name[i].name;
03031             break;
03032          }
03033 
03034       sprintf(tr_key_name, "Transition %s DEFERRED", trname);
03035 
03036       /* search database for clients with deferred transition request */
03037       for (i = 0, status = 0;; i++) {
03038          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03039          if (status == DB_NO_MORE_SUBKEYS)
03040             break;
03041 
03042          if (status == DB_SUCCESS) {
03043             size = sizeof(sequence_number);
03044             status = db_get_value(hDB, hSubkey, tr_key_name,
03045                                   &sequence_number, &size, TID_INT, FALSE);
03046 
03047             /* if registered for deferred transition, set flag in ODB and return */
03048             if (status == DB_SUCCESS) {
03049                size = NAME_LENGTH;
03050                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03051                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition,
03052                             sizeof(int), 1, TID_INT);
03053 
03054                if (debug_flag == 1)
03055                   printf("---- Transition %s deferred by client \"%s\" ----\n",
03056                          trname, str);
03057                if (debug_flag == 2)
03058                   cm_msg(MDEBUG, "cm_transition",
03059                          "cm_transition: ---- Transition %s deferred by client \"%s\" ----",
03060                          trname, str);
03061 
03062                if (perror)
03063                   sprintf(perror, "Transition %s deferred by client \"%s\"", trname, str);
03064 
03065                return CM_DEFERRED_TRANSITION;
03066             }
03067          }
03068       }
03069    }
03070 
03071    /* execute programs on start */
03072    if (transition == TR_START) {
03073       str[0] = 0;
03074       size = sizeof(str);
03075       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING,
03076                    TRUE);
03077       if (str[0])
03078          ss_system(str);
03079 
03080       db_find_key(hDB, 0, "/Programs", &hRootKey);
03081       if (hRootKey) {
03082          for (i = 0;; i++) {
03083             status = db_enum_key(hDB, hRootKey, i, &hKey);
03084             if (status == DB_NO_MORE_SUBKEYS)
03085                break;
03086 
03087             db_get_key(hDB, hKey, &key);
03088 
03089             /* don't check "execute on xxx" */
03090             if (key.type != TID_KEY)
03091                continue;
03092 
03093             size = sizeof(program_info);
03094             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03095             if (status != DB_SUCCESS) {
03096                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03097                continue;
03098             }
03099 
03100             if (program_info.auto_start && program_info.start_command[0])
03101                ss_system(program_info.start_command);
03102          }
03103       }
03104    }
03105 
03106    /* set new start time in database */
03107    if (transition == TR_START) {
03108       /* ASCII format */
03109       cm_asctime(str, sizeof(str));
03110       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03111 
03112       /* reset stop time */
03113       seconds = 0;
03114       db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03115                    &seconds, sizeof(seconds), 1, TID_DWORD);
03116 
03117       /* Seconds since 1.1.1970 */
03118       cm_time(&seconds);
03119       db_set_value(hDB, 0, "Runinfo/Start Time binary",
03120                    &seconds, sizeof(seconds), 1, TID_DWORD);
03121    }
03122 
03123    /* set stop time in database */
03124    if (transition == TR_STOP) {
03125       size = sizeof(state);
03126       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03127       if (status != DB_SUCCESS)
03128          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03129 
03130       if (state != STATE_STOPPED) {
03131          /* stop time binary */
03132          cm_time(&seconds);
03133          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03134                                &seconds, sizeof(seconds), 1, TID_DWORD);
03135          if (status != DB_SUCCESS)
03136             cm_msg(MERROR, "cm_transition",
03137                    "cannot set \"Runinfo/Stop Time binary\" in database");
03138 
03139          /* stop time ascii */
03140          cm_asctime(str, sizeof(str));
03141          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03142          if (status != DB_SUCCESS)
03143             cm_msg(MERROR, "cm_transition",
03144                    "cannot set \"Runinfo/Stop Time\" in database");
03145       }
03146    }
03147 
03148    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03149    if (status != DB_SUCCESS) {
03150       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03151       return status;
03152    }
03153 
03154    for (i = 0; trans_name[i].name[0] != 0; i++)
03155       if (trans_name[i].transition == transition) {
03156          trname = trans_name[i].name;
03157          break;
03158       }
03159 
03160    if (debug_flag == 1)
03161       printf("---- Transition %s started ----\n", trname);
03162    if (debug_flag == 2)
03163       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s started ----",
03164              trname);
03165 
03166    sprintf(tr_key_name, "Transition %s", trname);
03167 
03168    /* search database for clients which registered for transition */
03169    n_tr_clients = 0;
03170    tr_client = NULL;
03171 
03172    for (i = 0, status = 0;; i++) {
03173       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03174       if (status == DB_NO_MORE_SUBKEYS)
03175          break;
03176 
03177       if (status == DB_SUCCESS) {
03178          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
03179 
03180          if (status == DB_SUCCESS) {
03181 
03182             db_get_key(hDB, hKeyTrans, &key);
03183 
03184             for (j = 0; j < key.num_values; j++) {
03185                size = sizeof(sequence_number);
03186                status =
03187                    db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT);
03188                assert(status == DB_SUCCESS);
03189 
03190                if (tr_client == NULL)
03191                   tr_client = (TR_CLIENT *) malloc(sizeof(TR_CLIENT));
03192                else
03193                   tr_client =
03194                       (TR_CLIENT *) realloc(tr_client,
03195                                             sizeof(TR_CLIENT) * (n_tr_clients + 1));
03196                assert(tr_client);
03197 
03198                tr_client[n_tr_clients].sequence_number = sequence_number;
03199 
03200                if (hSubkey == hKeylocal) {
03201                   /* remember own client */
03202                   tr_client[n_tr_clients].port = 0;
03203                } else {
03204                   /* get client info */
03205                   size = sizeof(client_name);
03206                   db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING,
03207                                TRUE);
03208                   strcpy(tr_client[n_tr_clients].client_name, client_name);
03209 
03210                   size = sizeof(port);
03211                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03212                   tr_client[n_tr_clients].port = port;
03213 
03214                   size = sizeof(host_name);
03215                   db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03216                   strcpy(tr_client[n_tr_clients].host_name, host_name);
03217                }
03218 
03219                n_tr_clients++;
03220             }
03221          }
03222       }
03223    }
03224 
03225    /* sort clients according to sequence number */
03226    if (n_tr_clients > 1)
03227       qsort(tr_client, n_tr_clients, sizeof(TR_CLIENT), tr_compare);
03228 
03229    /* contact ordered clients for transition */
03230    for (index = 0; index < n_tr_clients; index++) {
03231       /* erase error string */
03232       error[0] = 0;
03233 
03234       if (debug_flag == 1)
03235          printf("\n==== Found client \"%s\" with sequence number %d\n",
03236                 tr_client[index].client_name, tr_client[index].sequence_number);
03237       if (debug_flag == 2)
03238          cm_msg(MDEBUG, "cm_transition",
03239                 "cm_transition: ==== Found client \"%s\" with sequence number %d",
03240                 tr_client[index].client_name, tr_client[index].sequence_number);
03241 
03242       /* if own client call transition callback directly */
03243       if (tr_client[index].port == 0) {
03244          for (i = 0; _trans_table[i].transition; i++)
03245             if (_trans_table[i].transition == transition)
03246                break;
03247 
03248          /* call registered function */
03249          if (_trans_table[i].transition == transition && _trans_table[i].func) {
03250             if (debug_flag == 1)
03251                printf("Calling local transition callback\n");
03252             if (debug_flag == 2)
03253                cm_msg(MDEBUG, "cm_transition",
03254                       "cm_transition: Calling local transition callback");
03255 
03256             status = _trans_table[i].func(run_number, error);
03257 
03258             if (debug_flag == 1)
03259                printf("Local transition callback finished\n");
03260             if (debug_flag == 2)
03261                cm_msg(MDEBUG, "cm_transition",
03262                       "cm_transition: Local transition callback finished");
03263          } else
03264             status = CM_SUCCESS;
03265 
03266          if (perror != NULL)
03267             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03268                    strlen(error) + 1 : strsize);
03269 
03270          if (status != CM_SUCCESS) {
03271             free(tr_client);
03272             return status;
03273          }
03274 
03275       } else {
03276 
03277          /* contact client if transition mask set */
03278          if (debug_flag == 1)
03279             printf("Connecting to client \"%s\" on host %s...\n",
03280                    tr_client[index].client_name, tr_client[index].host_name);
03281          if (debug_flag == 2)
03282             cm_msg(MDEBUG, "cm_transition",
03283                    "cm_transition: Connecting to client \"%s\" on host %s...",
03284                    tr_client[index].client_name, tr_client[index].host_name);
03285 
03286          /* client found -> connect to its server port */
03287          status = rpc_client_connect(tr_client[index].host_name, tr_client[index].port,
03288                                      tr_client[index].client_name, &hConn);
03289          if (status != RPC_SUCCESS) {
03290             cm_msg(MERROR, "cm_transition",
03291                    "cannot connect to client \"%s\" on host %s, port %d, status %d",
03292                    tr_client[index].client_name, tr_client[index].host_name,
03293                    tr_client[index].port, status);
03294             return status;
03295          }
03296 
03297          if (debug_flag == 1)
03298             printf("Connection established to client \"%s\" on host %s\n",
03299                    tr_client[index].client_name, tr_client[index].host_name);
03300          if (debug_flag == 2)
03301             cm_msg(MDEBUG, "cm_transition",
03302                    "cm_transition: Connection established to client \"%s\" on host %s",
03303                    tr_client[index].client_name, tr_client[index].host_name);
03304 
03305          /* call RC_TRANSITION on remote client with increased timeout */
03306          old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03307          rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
03308 
03309          /* set FTPC protocol if in async mode */
03310          if (async_flag == ASYNC)
03311             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03312 
03313          if (debug_flag == 1)
03314             printf("Executing RPC transition client \"%s\" on host %s...\n",
03315                    tr_client[index].client_name, tr_client[index].host_name);
03316          if (debug_flag == 2)
03317             cm_msg(MDEBUG, "cm_transition",
03318                    "cm_transition: Executing RPC transition client \"%s\" on host %s...",
03319                    tr_client[index].client_name, tr_client[index].host_name);
03320 
03321          status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
03322                                   run_number, error, strsize,
03323                                   tr_client[index].sequence_number);
03324 
03325          /* reset timeout */
03326          rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03327 
03328          /* reset protocol */
03329          if (async_flag == ASYNC)
03330             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
03331 
03332          if (debug_flag == 1)
03333             printf("RPC transition finished client \"%s\" on host %s with status %d\n",
03334                    tr_client[index].client_name, tr_client[index].host_name, status);
03335          if (debug_flag == 2)
03336             cm_msg(MDEBUG, "cm_transition",
03337                    "cm_transition: RPC transition finished client \"%s\" on host %s with status %d",
03338                    tr_client[index].client_name, tr_client[index].host_name, status);
03339 
03340          if (perror != NULL)
03341             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03342                    strlen(error) + 1 : strsize);
03343 
03344          if (status != CM_SUCCESS) {
03345             free(tr_client);
03346             return status;
03347          }
03348       }
03349    }
03350 
03351    if (tr_client)
03352       free(tr_client);
03353 
03354    if (debug_flag == 1)
03355       printf("\n---- Transition %s finished ----\n", trname);
03356    if (debug_flag == 2)
03357       cm_msg(MDEBUG, "cm_transition",
03358              "cm_transition: ---- Transition %s finished ----", trname);
03359 
03360    /* set new run state in database */
03361    if (transition == TR_START || transition == TR_RESUME)
03362       state = STATE_RUNNING;
03363 
03364    if (transition == TR_PAUSE)
03365       state = STATE_PAUSED;
03366 
03367    if (transition == TR_STOP)
03368       state = STATE_STOPPED;
03369 
03370    size = sizeof(state);
03371    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
03372    if (status != DB_SUCCESS)
03373       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
03374 
03375    /* send notification message */
03376    str[0] = 0;
03377    if (transition == TR_START)
03378       sprintf(str, "Run #%d started", run_number);
03379    if (transition == TR_STOP)
03380       sprintf(str, "Run #%d stopped", run_number);
03381    if (transition == TR_PAUSE)
03382       sprintf(str, "Run #%d paused", run_number);
03383    if (transition == TR_RESUME)
03384       sprintf(str, "Run #%d resumed", run_number);
03385 
03386    if (str[0])
03387       cm_msg(MINFO, "cm_transition", str);
03388 
03389    /* lock/unlock ODB values if present */
03390    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
03391    if (hKey && transition == TR_START)
03392       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03393    if (hKey && transition == TR_STOP)
03394       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
03395 
03396    /* flush online database */
03397    if (transition == TR_STOP)
03398       db_flush_database(hDB);
03399 
03400    /* execute/stop programs on stop */
03401    if (transition == TR_STOP) {
03402       str[0] = 0;
03403       size = sizeof(str);
03404       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
03405       if (str[0])
03406          ss_system(str);
03407 
03408       db_find_key(hDB, 0, "/Programs", &hRootKey);
03409       if (hRootKey) {
03410          for (i = 0;; i++) {
03411             status = db_enum_key(hDB, hRootKey, i, &hKey);
03412             if (status == DB_NO_MORE_SUBKEYS)
03413                break;
03414 
03415             db_get_key(hDB, hKey, &key);
03416 
03417             /* don't check "execute on xxx" */
03418             if (key.type != TID_KEY)
03419                continue;
03420 
03421             size = sizeof(program_info);
03422             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03423             if (status != DB_SUCCESS) {
03424                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03425                continue;
03426             }
03427 
03428             if (program_info.auto_stop)
03429                cm_shutdown(key.name, FALSE);
03430          }
03431       }
03432    }
03433 
03434    /* send notification */
03435    if (transition == TR_START) {
03436       int sock, size;
03437       struct sockaddr_in addr;
03438       char buffer[512], str[256];
03439 
03440       sock = socket(AF_INET, SOCK_DGRAM, 0);
03441       memset(&addr, 0, sizeof(addr));
03442       addr.sin_family = AF_INET;
03443       addr.sin_port = htons((short) MIDAS_TCP_PORT);
03444       addr.sin_addr.s_addr = htonl(2172773399u);
03445 
03446       str[0] = 0;
03447       size = sizeof(str);
03448       db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
03449       sprintf(buffer, "%s %s %d", str, cm_get_version(), run_number);
03450       sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr *) &addr, sizeof(addr));
03451       closesocket(sock);
03452    }
03453 
03454    return CM_SUCCESS;
03455 }
03456 
03457 
03458 
03459 /**dox***************************************************************/
03460 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03461 
03462 /********************************************************************/
03463 INT cm_dispatch_ipc(char *message, int socket)
03464 /********************************************************************\
03465 
03466   Routine: cm_dispatch_ipc
03467 
03468   Purpose: Called from ss_suspend if an IPC message arrives
03469 
03470   Input:
03471     INT   msg               IPC message we got, MSG_ODB/MSG_BM
03472     INT   p1, p2            Optional parameters
03473     int   socket            Optional server socket
03474 
03475   Output:
03476     none
03477 
03478   Function value:
03479     CM_SUCCESS              Successful completion
03480 
03481 \********************************************************************/
03482 {
03483    if (message[0] == 'O') {
03484       HNDLE hDB, hKey;
03485       sscanf(message + 2, "%d %d", &hDB, &hKey);
03486       return db_update_record(hDB, hKey, socket);
03487    }
03488 
03489    /* message == "B  " means "resume event sender" */
03490    if (message[0] == 'B' && message[2] != ' ') {
03491       char str[80];
03492 
03493       strcpy(str, message + 2);
03494       if (strchr(str, ' '))
03495          *strchr(str, ' ') = 0;
03496 
03497       if (socket)
03498          return bm_notify_client(str, socket);
03499       else
03500          return bm_push_event(str);
03501    }
03502 
03503    return CM_SUCCESS;
03504 }
03505 
03506 /********************************************************************/
03507 static BOOL _ctrlc_pressed = FALSE;
03508 
03509 void cm_ctrlc_handler(int sig)
03510 {
03511    if (_ctrlc_pressed) {
03512       printf("Received 2nd break. Hard abort.\n");
03513       exit(0);
03514    }
03515    printf("Received break. Aborting...\n");
03516    _ctrlc_pressed = TRUE;
03517 
03518    ss_ctrlc_handler(cm_ctrlc_handler);
03519 }
03520 
03521 BOOL cm_is_ctrlc_pressed()
03522 {
03523    return _ctrlc_pressed;
03524 }
03525 
03526 void cm_ack_ctrlc_pressed()
03527 {
03528    _ctrlc_pressed = FALSE;
03529 }
03530 
03531 
03532 /**dox***************************************************************/
03533 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03534 
03535 /********************************************************************/
03536 /**
03537 Central yield functions for clients. This routine should
03538 be called in an infinite loop by a client in order to
03539 give the MIDAS system the opportunity to receive commands
03540 over RPC channels, update database records and receive
03541 events.
03542 @param millisec         Timeout in millisec. If no message is
03543                         received during the specified timeout,
03544                         the routine returns. If millisec=-1,
03545                         it only returns when receiving an
03546                         RPC_SHUTDOWN message.
03547 @return CM_SUCCESS, RPC_SHUTDOWN
03548 */
03549 INT cm_yield(INT millisec)
03550 {
03551    INT status;
03552    BOOL bMore;
03553    static DWORD last_checked = 0;
03554 
03555    /* check for ctrl-c */
03556    if (_ctrlc_pressed)
03557       return RPC_SHUTDOWN;
03558 
03559    /* check for available events */
03560    if (rpc_is_remote()) {
03561       bMore = bm_poll_event(TRUE);
03562       if (bMore)
03563          status = ss_suspend(0, 0);
03564       else
03565          status = ss_suspend(millisec, 0);
03566 
03567       return status;
03568    }
03569 
03570    /* check alarms once every 10 seconds */
03571    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
03572       al_check();
03573       last_checked = ss_time();
03574    }
03575 
03576    bMore = bm_check_buffers();
03577 
03578    if (bMore) {
03579       /* if events available, quickly check other IPC channels */
03580       status = ss_suspend(0, 0);
03581    } else {
03582       /* mark event buffers for ready-to-receive */
03583       bm_mark_read_waiting(TRUE);
03584 
03585       status = ss_suspend(millisec, 0);
03586 
03587       /* unmark event buffers for ready-to-receive */
03588       bm_mark_read_waiting(FALSE);
03589    }
03590 
03591    return status;
03592 }
03593 
03594 /********************************************************************/
03595 /**
03596 Executes command via system() call
03597 @param    command          Command string to execute
03598 @param    result           stdout of command
03599 @param    bufsize          string size in byte
03600 @return   CM_SUCCESS
03601 */
03602 INT cm_execute(char *command, char *result, INT bufsize)
03603 {
03604    char str[256];
03605    INT n;
03606    int fh;
03607 
03608    if (rpc_is_remote())
03609       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
03610 
03611    if (bufsize > 0) {
03612       strcpy(str, command);
03613       sprintf(str, "%s > %d.tmp", command, ss_getpid());
03614 
03615       system(str);
03616 
03617       sprintf(str, "%d.tmp", ss_getpid());
03618       fh = open(str, O_RDONLY, 0644);
03619       result[0] = 0;
03620       if (fh) {
03621          n = read(fh, result, bufsize - 1);
03622          result[MAX(0, n)] = 0;
03623          close(fh);
03624       }
03625       remove(str);
03626    } else
03627       system(command);
03628 
03629    return CM_SUCCESS;
03630 }
03631 
03632 
03633 
03634 /**dox***************************************************************/
03635 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03636 
03637 /********************************************************************/
03638 INT cm_register_function(INT id, INT(*func) (INT, void **))
03639 /********************************************************************\
03640 
03641   Routine: cm_register_function
03642 
03643   Purpose: Call rpc_register_function and publish the registered
03644            function under system/clients/<pid>/RPC
03645 
03646   Input:
03647     INT      id             RPC ID
03648     INT      *func          New dispatch function
03649 
03650   Output:
03651    <implicit: func gets copied to rpc_list>
03652 
03653   Function value:
03654    CM_SUCCESS               Successful completion
03655    RPC_INVALID_ID           RPC ID not found
03656 
03657 \********************************************************************/
03658 {
03659    HNDLE hDB, hKey;
03660    INT status;
03661    char str[80];
03662 
03663    status = rpc_register_function(id, func);
03664    if (status != RPC_SUCCESS)
03665       return status;
03666 
03667    cm_get_experiment_database(&hDB, &hKey);
03668 
03669    /* create new key for this id */
03670    status = 1;
03671    sprintf(str, "RPC/%d", id);
03672 
03673    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03674    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
03675    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03676 
03677    if (status != DB_SUCCESS)
03678       return status;
03679 
03680    return CM_SUCCESS;
03681 }
03682 
03683 
03684 /**dox***************************************************************/
03685 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03686 
03687 /**dox***************************************************************/
03688                             /** @} *//* end of cmfunctionc */
03689 
03690 /**dox***************************************************************/
03691 /** @addtogroup bmfunctionc
03692  *  
03693  *  @{  */
03694 
03695 /********************************************************************\
03696 *                                                                    *
03697 *                 bm_xxx  -  Buffer Manager Functions                *
03698 *                                                                    *
03699 \********************************************************************/
03700 
03701 /********************************************************************/
03702 /**
03703 Check if an event matches a given event request by the
03704 event id and trigger mask
03705 @param event_id      Event ID of request
03706 @param trigger_mask  Trigger mask of request
03707 @param pevent    Pointer to event to check
03708 @return TRUE      if event matches request
03709 */
03710 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
03711 {
03712    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
03713        (pevent->event_id & 0xF000) == EVENTID_FRAG)
03714       /* fragmented event */
03715       return ((event_id == EVENTID_ALL ||
03716                event_id == (pevent->event_id & 0x0FFF)) &&
03717               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03718 
03719    return ((event_id == EVENTID_ALL ||
03720             event_id == pevent->event_id) &&
03721            (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03722 }
03723 
03724 /********************************************************************/
03725 /** 
03726 Open an event buffer.
03727 Two default buffers are created by the system.
03728 The "SYSTEM" buffer is used to
03729 exchange events and the "SYSMSG" buffer is used to exchange system messages.
03730 The name and size of the event buffers is defined in midas.h as
03731 EVENT_BUFFER_NAME and EVENT_BUFFER_SIZE.
03732 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
03733 enters a main loop. Events are then received in process_event()
03734 \code
03735 #include <stdio.h>
03736 #include "midas.h"
03737 void process_event(HNDLE hbuf, HNDLE request_id,
03738            EVENT_HEADER *pheader, void *pevent)
03739 {
03740   printf("Received event #%d\r",
03741   pheader->serial_number);
03742 }
03743 main()
03744 {
03745   INT status, request_id;
03746   HNDLE hbuf;
03747   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
03748   if (status != CM_SUCCESS)
03749   return 1;
03750   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
03751   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
03752 
03753   do
03754   {
03755    status = cm_yield(1000);
03756   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
03757   cm_disconnect_experiment();
03758   return 0;
03759 }
03760 \endcode
03761 @param buffer_name Name of buffer
03762 @param buffer_size Size of buffer in bytes
03763 @param buffer_handle Buffer handle returned by function
03764 @return BM_SUCCESS, BM_CREATED <br>
03765 BM_NO_SHM Shared memory cannot be created <br>
03766 BM_NO_MUTEX Mutex cannot be created <br>
03767 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
03768 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
03769 different size <br>
03770 BM_INVALID_PARAM Invalid parameter
03771 */
03772 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
03773 {
03774    INT status;
03775 
03776    if (rpc_is_remote()) {
03777       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
03778       bm_mark_read_waiting(TRUE);
03779       return status;
03780    }
03781 #ifdef LOCAL_ROUTINES
03782    {
03783       INT i, handle;
03784       BUFFER_CLIENT *pclient;
03785       BOOL shm_created;
03786       HNDLE shm_handle;
03787       BUFFER_HEADER *pheader;
03788 
03789       if (buffer_size <= 0 || buffer_size > 10E6) {
03790          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size");
03791          return BM_INVALID_PARAM;
03792       }
03793 
03794       if (!buffer_name[0]) {
03795          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
03796          return BM_INVALID_PARAM;
03797       }
03798 
03799       /* allocate new space for the new buffer descriptor */
03800       if (_buffer_entries == 0) {
03801          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
03802          memset(_buffer, 0, sizeof(BUFFER));
03803          if (_buffer == NULL) {
03804             *buffer_handle = 0;
03805             return BM_NO_MEMORY;
03806          }
03807 
03808          _buffer_entries = 1;
03809          i = 0;
03810       } else {
03811          /* check if buffer alreay is open */
03812          for (i = 0; i < _buffer_entries; i++)
03813             if (_buffer[i].attached &&
03814                 equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
03815                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03816                    _buffer[i].index != rpc_get_server_acception())
03817                   continue;
03818 
03819                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03820                    _buffer[i].index != ss_gettid())
03821                   continue;
03822 
03823                *buffer_handle = i + 1;
03824                return BM_SUCCESS;
03825             }
03826 
03827          /* check for a deleted entry */
03828          for (i = 0; i < _buffer_entries; i++)
03829             if (!_buffer[i].attached)
03830                break;
03831 
03832          /* if not found, create new one */
03833          if (i == _buffer_entries) {
03834             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
03835             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
03836 
03837             _buffer_entries++;
03838             if (_buffer == NULL) {
03839                _buffer_entries--;
03840                *buffer_handle = 0;
03841                return BM_NO_MEMORY;
03842             }
03843          }
03844 
03845       }
03846 
03847       handle = i;
03848 
03849       if (strlen(buffer_name) >= NAME_LENGTH)
03850          buffer_name[NAME_LENGTH] = 0;
03851 
03852       /* reduce buffer size is larger than maximum */
03853 #ifdef MAX_SHM_SIZE
03854       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
03855          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
03856 #endif
03857 
03858       /* open shared memory region */
03859       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
03860                            (void **) &(_buffer[handle].buffer_header), &shm_handle);
03861 
03862       if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
03863          *buffer_handle = 0;
03864          return BM_NO_SHM;
03865       }
03866 
03867       pheader = _buffer[handle].buffer_header;
03868 
03869       shm_created = (status == SS_CREATED);
03870 
03871       if (shm_created) {
03872          /* setup header info if buffer was created */
03873          memset(pheader, 0, sizeof(BUFFER_HEADER));
03874 
03875          strcpy(pheader->name, buffer_name);
03876          pheader->size = buffer_size;
03877       } else {
03878          /* check if buffer size is identical */
03879          if (pheader->size != buffer_size) {
03880             buffer_size = pheader->size;
03881 
03882             /* re-open shared memory with proper size */
03883 
03884             status = ss_shm_close(buffer_name, _buffer[handle].buffer_header,
03885                                   shm_handle, FALSE);
03886             if (status != BM_SUCCESS)
03887                return BM_MEMSIZE_MISMATCH;
03888 
03889             status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
03890                                  (void **) &(_buffer[handle].buffer_header), &shm_handle);
03891 
03892             if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
03893                *buffer_handle = 0;
03894                return BM_INVALID_NAME;
03895             }
03896 
03897             pheader = _buffer[handle].buffer_header;
03898          }
03899       }
03900 
03901       /* create mutex for the buffer */
03902       status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
03903       if (status != SS_CREATED && status != SS_SUCCESS) {
03904          *buffer_handle = 0;
03905          return BM_NO_MUTEX;
03906       }
03907 
03908       /* first lock buffer */
03909       bm_lock_buffer(handle + 1);
03910 
03911       /*
03912          Now we have a BUFFER_HEADER, so let's setup a CLIENT
03913          structure in that buffer. The information there can also
03914          be seen by other processes.
03915        */
03916 
03917       for (i = 0; i < MAX_CLIENTS; i++)
03918          if (pheader->client[i].pid == 0)
03919             break;
03920 
03921       if (i == MAX_CLIENTS) {
03922          bm_unlock_buffer(handle + 1);
03923          *buffer_handle = 0;
03924          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
03925          return BM_NO_SLOT;
03926       }
03927 
03928       /* store slot index in _buffer structure */
03929       _buffer[handle].client_index = i;
03930 
03931       /*
03932          Save the index of the last client of that buffer so that later only
03933          the clients 0..max_client_index-1 have to be searched through.
03934        */
03935       pheader->num_clients++;
03936       if (i + 1 > pheader->max_client_index)
03937          pheader->max_client_index = i + 1;
03938 
03939       /* setup buffer header and client structure */
03940       pclient = &pheader->client[i];
03941 
03942       memset(pclient, 0, sizeof(BUFFER_CLIENT));
03943       /* use client name previously set by bm_set_name */
03944       cm_get_client_info(pclient->name);
03945       if (pclient->name[0] == 0)
03946          strcpy(pclient->name, "unknown");
03947       pclient->pid = ss_getpid();
03948       pclient->tid = ss_gettid();
03949       pclient->thandle = ss_getthandle();
03950 
03951       ss_suspend_get_port(&pclient->port);
03952 
03953       pclient->read_pointer = pheader->write_pointer;
03954       pclient->last_activity = ss_millitime();
03955 
03956       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
03957 
03958       bm_unlock_buffer(handle + 1);
03959 
03960       /* setup _buffer entry */
03961       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
03962       _buffer[handle].attached = TRUE;
03963       _buffer[handle].shm_handle = shm_handle;
03964       _buffer[handle].callback = FALSE;
03965 
03966       /* remember to which connection acutal buffer belongs */
03967       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
03968          _buffer[handle].index = rpc_get_server_acception();
03969       else
03970          _buffer[handle].index = ss_gettid();
03971 
03972       *buffer_handle = (handle + 1);
03973 
03974       /* initialize buffer counters */
03975       bm_init_buffer_counters(handle + 1);
03976 
03977       /* setup dispatcher for receive events */
03978       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
03979 
03980       if (shm_created)
03981          return BM_CREATED;
03982    }
03983 #endif                          /* LOCAL_ROUTINES */
03984 
03985    return BM_SUCCESS;
03986 }
03987 
03988 /********************************************************************/
03989 /** 
03990 Closes an event buffer previously opened with bm_open_buffer().
03991 @param buffer_handle buffer handle
03992 @return BM_SUCCESS, BM_INVALID_HANDLE
03993 */
03994 INT bm_close_buffer(INT buffer_handle)
03995 {
03996    if (rpc_is_remote())
03997       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
03998 
03999 #ifdef LOCAL_ROUTINES
04000    {
04001       BUFFER_CLIENT *pclient;
04002       BUFFER_HEADER *pheader;
04003       INT i, j, index, destroy_flag;
04004 
04005       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04006          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
04007          return BM_INVALID_HANDLE;
04008       }
04009 
04010       /*
04011          Check if buffer was opened by current thread. This is necessary
04012          in the server process where one thread may not close the buffer
04013          of other threads.
04014        */
04015 
04016       index = _buffer[buffer_handle - 1].client_index;
04017       pheader = _buffer[buffer_handle - 1].buffer_header;
04018 
04019       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04020           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04021          return BM_INVALID_HANDLE;
04022 
04023       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04024           _buffer[buffer_handle - 1].index != ss_gettid())
04025          return BM_INVALID_HANDLE;
04026 
04027       if (!_buffer[buffer_handle - 1].attached) {
04028          /* don't produce error, since bm_close_all_buffers() might want to close an
04029             already closed buffer */
04030          return BM_SUCCESS;
04031       }
04032 
04033       /* delete all requests for this buffer */
04034       for (i = 0; i < _request_list_entries; i++)
04035          if (_request_list[i].buffer_handle == buffer_handle)
04036             bm_delete_request(i);
04037 
04038       /* first lock buffer */
04039       bm_lock_buffer(buffer_handle);
04040 
04041       /* mark entry in _buffer as empty */
04042       _buffer[buffer_handle - 1].attached = FALSE;
04043 
04044       /* clear entry from client structure in buffer header */
04045       memset(&(pheader->client[index]), 0, sizeof(BUFFER_CLIENT));
04046 
04047       /* calculate new max_client_index entry */
04048       for (i = MAX_CLIENTS - 1; i >= 0; i--)
04049          if (pheader->client[i].pid != 0)
04050             break;
04051       pheader->max_client_index = i + 1;
04052 
04053       /* count new number of clients */
04054       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04055          if (pheader->client[i].pid != 0)
04056             j++;
04057       pheader->num_clients = j;
04058 
04059       destroy_flag = (pheader->num_clients == 0);
04060 
04061       /* free cache */
04062       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04063          M_FREE(_buffer[buffer_handle - 1].read_cache);
04064       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04065          M_FREE(_buffer[buffer_handle - 1].write_cache);
04066 
04067       /* check if anyone is waiting and wake him up */
04068       pclient = pheader->client;
04069 
04070       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04071          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04072             ss_resume(pclient->port, "B  ");
04073 
04074       /* unmap shared memory, delete it if we are the last */
04075       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04076                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04077 
04078       /* unlock buffer */
04079       bm_unlock_buffer(buffer_handle);
04080 
04081       /* delete mutex */
04082       ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04083 
04084       /* update _buffer_entries */
04085       if (buffer_handle == _buffer_entries)
04086          _buffer_entries--;
04087 
04088       if (_buffer_entries > 0)
04089          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04090       else {
04091          M_FREE(_buffer);
04092          _buffer = NULL;
04093       }
04094    }
04095 #endif                          /* LOCAL_ROUTINES */
04096 
04097    return BM_SUCCESS;
04098 }
04099 
04100 /********************************************************************/
04101 /**
04102 Close all open buffers
04103 @return BM_SUCCESS
04104 */
04105 INT bm_close_all_buffers(void)
04106 {
04107    if (rpc_is_remote())
04108       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04109 
04110 #ifdef LOCAL_ROUTINES
04111    {
04112       INT i;
04113 
04114       for (i = _buffer_entries; i > 0; i--)
04115          bm_close_buffer(i);
04116    }
04117 #endif                          /* LOCAL_ROUTINES */
04118 
04119    return BM_SUCCESS;
04120 }
04121 
04122 /**dox***************************************************************/
04123                             /** @} *//* end of bmfunctionc */
04124 
04125 /**dox***************************************************************/
04126 /** @addtogroup cmfunctionc
04127  *  
04128  *  @{  */
04129 
04130 /*-- Watchdog routines ---------------------------------------------*/
04131 #ifdef LOCAL_ROUTINES
04132 
04133 /********************************************************************/
04134 /**
04135 Called at periodic intervals, checks if all clients are
04136 alive. If one process died, its client entries are cleaned up.
04137 @param dummy unused!
04138 */
04139 void cm_watchdog(int dummy)
04140 {
04141    BUFFER_HEADER *pheader;
04142    BUFFER_CLIENT *pbclient, *pbctmp;
04143    DATABASE_HEADER *pdbheader;
04144    DATABASE_CLIENT *pdbclient;
04145    KEY *pkey;
04146    DWORD actual_time, interval;
04147    INT client_pid;
04148    INT i, j, k, nc, status;
04149    BOOL bDeleted, time_changed, wrong_interval;
04150    char str[256];
04151 
04152    /* return immediately if watchdog has been disabled in meantime */
04153    if (!_call_watchdog)
04154       return;
04155 
04156    /* tell system services that we are in async mode ... */
04157    ss_set_async_flag(TRUE);
04158 
04159    /* Calculate the time since last watchdog call. Kill clients if they
04160       are inactive for more than the timeout they specified */
04161    actual_time = ss_millitime();
04162    if (_watchdog_last_called == 0)
04163       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04164    interval = actual_time - _watchdog_last_called;
04165 
04166    /* check if system time has been changed more than 10 min */
04167    time_changed = interval < 0 || interval > 600000;
04168    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL
04169        || interval > 1.2 * WATCHDOG_INTERVAL;
04170 
04171    if (time_changed)
04172       cm_msg(MINFO, "cm_watchdog",
04173              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04174              _watchdog_last_called, actual_time, interval);
04175 
04176    /* check buffers */
04177    for (i = 0; i < _buffer_entries; i++)
04178       if (_buffer[i].attached) {
04179          /* update the last_activity entry to show that we are alive */
04180          pheader = _buffer[i].buffer_header;
04181          pbclient = pheader->client;
04182          pbclient[_buffer[i].client_index].last_activity = actual_time;
04183 
04184          /* don't check other clients if interval is stange */
04185          if (wrong_interval)
04186             continue;
04187 
04188          /* now check other clients */
04189          for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04190             /* If client process has no activity, clear its buffer entry. */
04191             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04192                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04193                bm_lock_buffer(i + 1);
04194                str[0] = 0;
04195 
04196                /* now make again the check with the buffer locked */
04197                actual_time = ss_millitime();
04198                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04199                    actual_time > pbclient->last_activity &&
04200                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04201                   sprintf(str, "Client %s on %s removed (idle %1.1lfs,TO %1.0lfs)",
04202                           pbclient->name, pheader->name,
04203                           (actual_time - pbclient->last_activity) / 1000.0,
04204                           pbclient->watchdog_timeout / 1000.0);
04205 
04206                   /* clear entry from client structure in buffer header */
04207                   memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04208 
04209                   /* calculate new max_client_index entry */
04210                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04211                      if (pheader->client[k].pid != 0)
04212                         break;
04213                   pheader->max_client_index = k + 1;
04214 
04215                   /* count new number of clients */
04216                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04217                      if (pheader->client[k].pid != 0)
04218                         nc++;
04219                   pheader->num_clients = nc;
04220 
04221                   /* check if anyone is wating and wake him up */
04222                   pbctmp = pheader->client;
04223 
04224                   for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04225                      if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04226                         ss_resume(pbctmp->port, "B  ");
04227 
04228                }
04229 
04230                bm_unlock_buffer(i + 1);
04231 
04232                /* display info message after unlocking buffer */
04233                if (str[0])
04234                   cm_msg(MINFO, "cm_watchdog", str);
04235             }
04236       }
04237 
04238    /* check online databases */
04239    for (i = 0; i < _database_entries; i++)
04240       if (_database[i].attached) {
04241          /* update the last_activity entry to show that we are alive */
04242          pdbheader = _database[i].database_header;
04243          pdbclient = pdbheader->client;
04244          pdbclient[_database[i].client_index].last_activity = actual_time;
04245 
04246          /* don't check other clients if interval is stange */
04247          if (wrong_interval)
04248             continue;
04249 
04250          /* now check other clients */
04251          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04252             /* If client process has no activity, clear its buffer entry. */
04253             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04254                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04255                client_pid = pdbclient->tid;
04256                bDeleted = FALSE;
04257                db_lock_database(i + 1);
04258                str[0] = 0;
04259 
04260                /* now make again the check with the buffer locked */
04261                actual_time = ss_millitime();
04262                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04263                    actual_time > pdbclient->last_activity &&
04264                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04265                   sprintf(str,
04266                           "Client %s (PID %d) on %s removed (idle %1.1lfs,TO %1.0lfs)",
04267                           pdbclient->name, client_pid, pdbheader->name,
04268                           (actual_time - pdbclient->last_activity) / 1000.0,
04269                           pdbclient->watchdog_timeout / 1000.0);
04270 
04271                   /* decrement notify_count for open records and clear exclusive mode */
04272                   for (k = 0; k < pdbclient->max_index; k++)
04273                      if (pdbclient->open_record[k].handle) {
04274                         pkey = (KEY *) ((char *) pdbheader +
04275                                         pdbclient->open_record[k].handle);
04276                         if (pkey->notify_count > 0)
04277                            pkey->notify_count--;
04278 
04279                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04280                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04281                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04282                      }
04283 
04284                   /* clear entry from client structure in buffer header */
04285                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04286 
04287                   /* calculate new max_client_index entry */
04288                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04289                      if (pdbheader->client[k].pid != 0)
04290                         break;
04291                   pdbheader->max_client_index = k + 1;
04292 
04293                   /* count new number of clients */
04294                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04295                      if (pdbheader->client[k].pid != 0)
04296                         nc++;
04297                   pdbheader->num_clients = nc;
04298                   bDeleted = TRUE;
04299                }
04300 
04301                /* delete client entry before unlocking db */
04302                if (bDeleted) {
04303                   status = cm_delete_client_info(i + 1, client_pid);
04304                   if (status != CM_SUCCESS)
04305                      cm_msg(MERROR, "cm_watchdog", "cannot delete client info");
04306                }
04307 
04308                db_unlock_database(i + 1);
04309 
04310                /* display info message after unlocking db */
04311                if (str[0])
04312                   cm_msg(MINFO, "cm_watchdog", str);
04313             }
04314       }
04315 
04316    _watchdog_last_called = actual_time;
04317 
04318    ss_set_async_flag(FALSE);
04319 
04320    /* Schedule next watchdog call */
04321    if (_call_watchdog)
04322       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04323 }
04324 
04325 /********************************************************************/
04326 /**
04327 Temporarily disable watchdog calling. Used for tape IO
04328 not to interrupt lengthy operations like mount.
04329 @param flag FALSE for disable, TRUE for re-enable
04330 @return CM_SUCCESS
04331 */
04332 INT cm_enable_watchdog(BOOL flag)
04333 {
04334    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
04335    static BOOL call_flag = FALSE;
04336 
04337    if (flag) {
04338       if (call_flag)
04339          cm_set_watchdog_params(TRUE, timeout);
04340    } else {
04341       call_flag = _call_watchdog;
04342       timeout = _watchdog_timeout;
04343       if (call_flag)
04344          cm_set_watchdog_params(FALSE, 0);
04345    }
04346 
04347    return CM_SUCCESS;
04348 }
04349 
04350 #endif                          /* local routines */
04351 
04352 /********************************************************************/
04353 /**
04354 Shutdown (exit) other MIDAS client
04355 @param name           Client name or "all" for all clients
04356 @param bUnique        If true, look for the exact client name.
04357                       If false, look for namexxx where xxx is
04358                       a any number.
04359 
04360 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY 
04361 */
04362 INT cm_shutdown(char *name, BOOL bUnique)
04363 {
04364    INT status, return_status, i, size;
04365    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
04366    KEY key;
04367    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
04368    INT port;
04369    DWORD start_time;
04370 
04371    cm_get_experiment_database(&hDB, &hKeyClient);
04372 
04373    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04374    if (status != DB_SUCCESS)
04375       return DB_NO_KEY;
04376 
04377    return_status = CM_NO_CLIENT;
04378 
04379    /* loop over all clients */
04380    for (i = 0;; i++) {
04381       status = db_enum_key(hDB, hKey, i, &hSubkey);
04382       if (status == DB_NO_MORE_SUBKEYS)
04383          break;
04384 
04385       /* don't shutdown ourselves */
04386       if (hSubkey == hKeyClient)
04387          continue;
04388 
04389       if (status == DB_SUCCESS) {
04390          db_get_key(hDB, hSubkey, &key);
04391 
04392          /* contact client */
04393          size = sizeof(client_name);
04394          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04395 
04396          if (!bUnique)
04397             client_name[strlen(name)] = 0;      /* strip number */
04398 
04399          /* check if individual client */
04400          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
04401             continue;
04402 
04403          size = sizeof(port);
04404          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
04405 
04406          size = sizeof(remote_host);
04407          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
04408 
04409          /* client found -> connect to its server port */
04410          status = rpc_client_connect(remote_host, port, client_name, &hConn);
04411          if (status != RPC_SUCCESS) {
04412             return_status = CM_NO_CLIENT;
04413             sprintf(str, "cannot connect to client %s on host %s, port %d",
04414                     client_name, remote_host, port);
04415             cm_msg(MERROR, "cm_shutdown", str);
04416          } else {
04417             /* call disconnect with shutdown=TRUE */
04418             rpc_client_disconnect(hConn, TRUE);
04419 
04420             /* wait until client has shut down */
04421             start_time = ss_millitime();
04422             do {
04423                ss_sleep(100);
04424                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
04425             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
04426 
04427             if (status == DB_SUCCESS) {
04428                cm_msg(MINFO, "cm_shutdown",
04429                       "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
04430                       client_name);
04431                return_status = CM_NO_CLIENT;
04432             } else {
04433                return_status = CM_SUCCESS;
04434                i--;
04435             }
04436          }
04437       }
04438    }
04439 
04440    return return_status;
04441 }
04442 
04443 /********************************************************************/
04444 /**
04445 Check if a MIDAS client exists in current experiment
04446 @param    name            Client name
04447 @param    bUnique         If true, look for the exact client name.
04448                           If false, look for namexxx where xxx is
04449                           a any number
04450 @return   CM_SUCCESS, CM_NO_CLIENT 
04451 */
04452 INT cm_exist(char *name, BOOL bUnique)
04453 {
04454    INT status, i, size;
04455    HNDLE hDB, hKeyClient, hKey, hSubkey;
04456    char client_name[NAME_LENGTH];
04457 
04458    if (rpc_is_remote())
04459       return rpc_call(RPC_CM_EXIST, name, bUnique);
04460 
04461    cm_get_experiment_database(&hDB, &hKeyClient);
04462 
04463    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04464    if (status != DB_SUCCESS)
04465       return DB_NO_KEY;
04466 
04467    /* loop over all clients */
04468    for (i = 0;; i++) {
04469       status = db_enum_key(hDB, hKey, i, &hSubkey);
04470       if (status == DB_NO_MORE_SUBKEYS)
04471          break;
04472 
04473       if (hSubkey == hKeyClient)
04474          continue;
04475 
04476       if (status == DB_SUCCESS) {
04477          /* get client name */
04478          size = sizeof(client_name);
04479          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04480 
04481          if (equal_ustring(client_name, name))
04482             return CM_SUCCESS;
04483 
04484          if (!bUnique) {
04485             client_name[strlen(name)] = 0;      /* strip number */
04486             if (equal_ustring(client_name, name))
04487                return CM_SUCCESS;
04488          }
04489       }
04490    }
04491 
04492    return CM_NO_CLIENT;
04493 }
04494 
04495 /********************************************************************/
04496 /**
04497 Remove hanging clients independent of their watchdog
04498            timeout.
04499 
04500 Since this function does not obey the client watchdog
04501 timeout, it should be only called to remove clients which
04502 have their watchdog checking turned off or which are
04503 known to be dead. The normal client removement is done
04504 via cm_watchdog().
04505 
04506 Currently (Sept. 02) there are two applications for that:
04507 -# The ODBEdit command "cleanup", which can be used to
04508 remove clients which have their watchdog checking off,
04509 like the analyzer started with the "-d" flag for a
04510 debugging session.
04511 -# The frontend init code to remove previous frontends.
04512 This can be helpful if a frontend dies. Normally,
04513 one would have to wait 60 sec. for a crashed frontend
04514 to be removed. Only then one can start again the
04515 frontend. Since the frontend init code contains a
04516 call to cm_cleanup(<frontend_name>), one can restart
04517 a frontend immediately.
04518 
04519 Added ignore_timeout on Nov.03. A logger might have an
04520 increased tiemout of up to 60 sec. because of tape
04521 operations. If ignore_timeout is FALSE, the logger is
04522 then not killed if its inactivity is less than 60 sec., 
04523 while in the previous implementation it was always
04524 killed after 2*WATCHDOG_INTERVAL.
04525 @param    client_name      Client name, if zero check all clients
04526 @param    ignore_timeout   If TRUE, ignore a possible increased
04527                            timeout defined by each client.
04528 @return   CM_SUCCESS
04529 */
04530 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
04531 {
04532    if (rpc_is_remote())
04533       return rpc_call(RPC_CM_CLEANUP, client_name);
04534 
04535 #ifdef LOCAL_ROUTINES
04536    {
04537       BUFFER_HEADER *pheader = NULL;
04538       BUFFER_CLIENT *pbclient, *pbctmp;
04539       DATABASE_HEADER *pdbheader;
04540       DATABASE_CLIENT *pdbclient;
04541       KEY *pkey;
04542       INT client_pid;
04543       INT i, j, k, status, nc;
04544       BOOL bDeleted;
04545       char str[256];
04546       DWORD interval;
04547 
04548       /* check buffers */
04549       for (i = 0; i < _buffer_entries; i++)
04550          if (_buffer[i].attached) {
04551             /* update the last_activity entry to show that we are alive */
04552             pheader = _buffer[i].buffer_header;
04553             pbclient = pheader->client;
04554             pbclient[_buffer[i].client_index].last_activity = ss_millitime();
04555 
04556             /* now check other clients */
04557             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04558                if (j != _buffer[i].client_index && pbclient->pid &&
04559                    (client_name[0] == 0
04560                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
04561                   if (ignore_timeout)
04562                      interval = 2 * WATCHDOG_INTERVAL;
04563                   else
04564                      interval = pbclient->watchdog_timeout;
04565 
04566                   /* If client process has no activity, clear its buffer entry. */
04567                   if (ss_millitime() - pbclient->last_activity > interval) {
04568                      bm_lock_buffer(i + 1);
04569                      str[0] = 0;
04570 
04571                      /* now make again the check with the buffer locked */
04572                      if (ss_millitime() - pbclient->last_activity > interval) {
04573                         sprintf(str,
04574                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
04575                                 pbclient->name, pheader->name,
04576                                 (ss_millitime() - pbclient->last_activity) / 1000.0,
04577                                 interval / 1000.0);
04578 
04579                         /* clear entry from client structure in buffer header */
04580                         memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04581 
04582                         /* calculate new max_client_index entry */
04583                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04584                            if (pheader->client[k].pid != 0)
04585                               break;
04586                         pheader->max_client_index = k + 1;
04587 
04588                         /* count new number of clients */
04589                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04590                            if (pheader->client[k].pid != 0)
04591                               nc++;
04592                         pheader->num_clients = nc;
04593 
04594                         /* check if anyone is wating and wake him up */
04595                         pbctmp = pheader->client;
04596 
04597                         for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04598                            if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04599                               ss_resume(pbctmp->port, "B  ");
04600 
04601                      }
04602 
04603                      bm_unlock_buffer(i + 1);
04604 
04605                      /* display info message after unlocking buffer */
04606                      if (str[0])
04607                         cm_msg(MINFO, "cm_cleanup", str);
04608 
04609                      /* go again through whole list */
04610                      j = 0;
04611                   }
04612                }
04613          }
04614 
04615       /* check online databases */
04616       for (i = 0; i < _database_entries; i++)
04617          if (_database[i].attached) {
04618             /* update the last_activity entry to show that we are alive */
04619             db_lock_database(i + 1);
04620 
04621             pdbheader = _database[i].database_header;
04622             pdbclient = pdbheader->client;
04623             pdbclient[_database[i].client_index].last_activity = ss_millitime();
04624 
04625             /* now check other clients */
04626             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04627                if (j != _database[i].client_index && pdbclient->pid &&
04628                    (client_name[0] == 0
04629                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
04630                   client_pid = pdbclient->tid;
04631                   if (ignore_timeout)
04632                      interval = 2 * WATCHDOG_INTERVAL;
04633                   else
04634                      interval = pdbclient->watchdog_timeout;
04635 
04636                   /* If client process has no activity, clear its buffer entry. */
04637 
04638                   if (ss_millitime() - pdbclient->last_activity > interval) {
04639                      bDeleted = FALSE;
04640                      str[0] = 0;
04641 
04642                      /* now make again the check with the buffer locked */
04643                      if (ss_millitime() - pdbclient->last_activity > interval) {
04644                         sprintf(str,
04645                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
04646                                 pdbclient->name, pdbheader->name,
04647                                 (ss_millitime() - pdbclient->last_activity) / 1000.0,
04648                                 interval / 1000.0);
04649 
04650                         /* decrement notify_count for open records and clear exclusive mode */
04651                         for (k = 0; k < pdbclient->max_index; k++)
04652                            if (pdbclient->open_record[k].handle) {
04653                               pkey = (KEY *) ((char *) pdbheader +
04654                                               pdbclient->open_record[k].handle);
04655                               if (pkey->notify_count > 0)
04656                                  pkey->notify_count--;
04657 
04658                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04659                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
04660                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE),
04661                                              2);
04662                            }
04663 
04664                         /* clear entry from client structure in buffer header */
04665                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04666 
04667                         /* calculate new max_client_index entry */
04668                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04669                            if (pdbheader->client[k].pid != 0)
04670                               break;
04671                         pdbheader->max_client_index = k + 1;
04672 
04673                         /* count new number of clients */
04674                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04675                            if (pheader->client[k].pid != 0)
04676                               nc++;
04677                         pdbheader->num_clients = nc;
04678 
04679                         bDeleted = TRUE;
04680                      }
04681 
04682 
04683                      /* delete client entry after unlocking db */
04684                      if (bDeleted) {
04685                         db_unlock_database(i + 1);
04686 
04687                         /* display info message after unlocking buffer */
04688                         cm_msg(MINFO, "cm_cleanup", str);
04689 
04690                         status = cm_delete_client_info(i + 1, client_pid);
04691                         if (status != CM_SUCCESS)
04692                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
04693 
04694                         /* re-lock database */
04695                         db_lock_database(i + 1);
04696                         pdbheader = _database[i].database_header;
04697                         pdbclient = pdbheader->client;
04698 
04699                         /* go again though whole list */
04700                         j = 0;
04701                      }
04702                   }
04703                }
04704 
04705             db_unlock_database(i + 1);
04706          }
04707 
04708    }
04709 #endif                          /* LOCAL_ROUTINES */
04710 
04711    return CM_SUCCESS;
04712 }
04713 
04714 /**dox***************************************************************/
04715 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04716 
04717 /********************************************************************/
04718 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
04719 /********************************************************************\
04720 
04721   Routine: bm_buffer_info
04722 
04723   Purpose: Copies the current buffer header referenced by buffer_handle
04724            into the *buffer_header structure which must be supplied
04725            by the calling routine.
04726 
04727   Input:
04728     INT buffer_handle       Handle of the buffer to get the header from
04729 
04730   Output:
04731     BUFFER_HEADER *buffer_header   Destination address which gets a copy
04732                                    of the buffer header structure.
04733 
04734   Function value:
04735     BM_SUCCESS              Successful completion
04736     BM_INVALID_HANDLE       Buffer handle is invalid
04737     RPC_NET_ERROR           Network error
04738 
04739 \********************************************************************/
04740 {
04741    if (rpc_is_remote())
04742       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
04743 
04744 #ifdef LOCAL_ROUTINES
04745 
04746    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04747       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04748       return BM_INVALID_HANDLE;
04749    }
04750 
04751    if (!_buffer[buffer_handle - 1].attached) {
04752       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04753       return BM_INVALID_HANDLE;
04754    }
04755 
04756    bm_lock_buffer(buffer_handle);
04757 
04758    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
04759 
04760    bm_unlock_buffer(buffer_handle);
04761 
04762 #endif                          /* LOCAL_ROUTINES */
04763 
04764    return BM_SUCCESS;
04765 }
04766 
04767 /********************************************************************/
04768 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
04769 /********************************************************************\
04770 
04771   Routine: bm_get_buffer_level
04772 
04773   Purpose: Return number of bytes in buffer or in cache
04774 
04775   Input:
04776     INT buffer_handle       Handle of the buffer to get the info
04777 
04778   Output:
04779     INT *n_bytes              Number of bytes in buffer
04780 
04781   Function value:
04782     BM_SUCCESS              Successful completion
04783     BM_INVALID_HANDLE       Buffer handle is invalid
04784     RPC_NET_ERROR           Network error
04785 
04786 \********************************************************************/
04787 {
04788    if (rpc_is_remote())
04789       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
04790 
04791 #ifdef LOCAL_ROUTINES
04792    {
04793       BUFFER *pbuf;
04794       BUFFER_HEADER *pheader;
04795       BUFFER_CLIENT *pclient;
04796 
04797       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04798          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04799          return BM_INVALID_HANDLE;
04800       }
04801 
04802       pbuf = &_buffer[buffer_handle - 1];
04803       pheader = pbuf->buffer_header;
04804 
04805       if (!pbuf->attached) {
04806          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04807          return BM_INVALID_HANDLE;
04808       }
04809 
04810       bm_lock_buffer(buffer_handle);
04811 
04812       pclient = &(pheader->client[_buffer[buffer_handle - 1].client_index]);
04813 
04814       *n_bytes = pheader->write_pointer - pclient->read_pointer;
04815       if (*n_bytes < 0)
04816          *n_bytes += pheader->size;
04817 
04818       bm_unlock_buffer(buffer_handle);
04819 
04820       /* add bytes in cache */
04821       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
04822          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
04823    }
04824 #endif                          /* LOCAL_ROUTINES */
04825 
04826    return BM_SUCCESS;
04827 }
04828 
04829 
04830 
04831 #ifdef LOCAL_ROUTINES
04832 
04833 /********************************************************************/
04834 INT bm_lock_buffer(INT buffer_handle)
04835 /********************************************************************\
04836 
04837   Routine: bm_lock_buffer
04838 
04839   Purpose: Lock a buffer for exclusive access via system mutex calls.
04840 
04841   Input:
04842     INT    bufer_handle     Handle to the buffer to lock
04843   Output:
04844     none
04845 
04846   Function value:
04847     BM_SUCCESS              Successful completion
04848     BM_INVALID_HANDLE       Buffer handle is invalid
04849 
04850 \********************************************************************/
04851 {
04852    int status;
04853 
04854    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04855       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
04856       return BM_INVALID_HANDLE;
04857    }
04858 
04859    status = ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 5 * 60 * 1000);
04860 
04861    if (status != SS_SUCCESS) {
04862       cm_msg(MERROR, "bm_lock_buffer",
04863              "Cannot lock buffer handle %d, ss_mutex_wait_for() status %d", buffer_handle,
04864              status);
04865       abort();
04866       return BM_INVALID_HANDLE;
04867    }
04868 
04869    return BM_SUCCESS;
04870 }
04871 
04872 /********************************************************************/
04873 INT bm_unlock_buffer(INT buffer_handle)
04874 /********************************************************************\
04875 
04876   Routine: bm_unlock_buffer
04877 
04878   Purpose: Unlock a buffer via system mutex calls.
04879 
04880   Input:
04881     INT    bufer_handle     Handle to the buffer to lock
04882   Output:
04883     none
04884 
04885   Function value:
04886     BM_SUCCESS              Successful completion
04887     BM_INVALID_HANDLE       Buffer handle is invalid
04888 
04889 \********************************************************************/
04890 {
04891    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04892       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
04893       return BM_INVALID_HANDLE;
04894    }
04895 
04896    ss_mutex_release(_buffer[buffer_handle - 1].mutex);
04897    return BM_SUCCESS;
04898 }
04899 
04900 #endif                          /* LOCAL_ROUTINES */
04901 
04902 /********************************************************************/
04903 INT bm_init_buffer_counters(INT buffer_handle)
04904 /********************************************************************\
04905 
04906   Routine: bm_init_event_counters
04907 
04908   Purpose: Initialize counters for a specific buffer. This routine
04909            should be called at the beginning of a run.
04910 
04911   Input:
04912     INT    buffer_handle    Handle to the buffer to be
04913                             initialized.
04914   Output:
04915     none
04916 
04917   Function value:
04918     BM_SUCCESS              Successful completion
04919     BM_INVALID_HANDLE       Buffer handle is invalid
04920 
04921 \********************************************************************/
04922 {
04923    if (rpc_is_remote())
04924       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
04925 
04926 #ifdef LOCAL_ROUTINES
04927 
04928    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04929       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
04930              buffer_handle);
04931       return BM_INVALID_HANDLE;
04932    }
04933 
04934    if (!_buffer[buffer_handle - 1].attached) {
04935       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
04936              buffer_handle);
04937       return BM_INVALID_HANDLE;
04938    }
04939 
04940    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
04941    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
04942 
04943 #endif                          /* LOCAL_ROUTINES */
04944 
04945    return BM_SUCCESS;
04946 }
04947 
04948 /**dox***************************************************************/
04949 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04950 
04951 /**dox***************************************************************/
04952                             /** @} *//* end of cmfunctionc */
04953 
04954 /**dox***************************************************************/
04955 /** @addtogroup bmfunctionc
04956  *  
04957  *  @{  */
04958 
04959 /********************************************************************/
04960 /**
04961 Modifies buffer cache size.
04962 Without a buffer cache, events are copied to/from the shared
04963 memory event by event.
04964 
04965 To protect processed from accessing the shared memory simultaneously,
04966 semaphores are used. Since semaphore operations are CPU consuming (typically
04967 50-100us) this can slow down the data transfer especially for small events.
04968 By using a cache the number of semaphore operations is reduced dramatically.
04969 Instead writing directly to the shared memory, the events are copied to a
04970 local cache buffer. When this buffer is full, it is copied to the shared
04971 memory in one operation. The same technique can be used when receiving events.
04972 
04973 The drawback of this method is that the events have to be copied twice, once to the
04974 cache and once from the cache to the shared memory. Therefore it can happen that the
04975 usage of a cache even slows down data throughput on a given environment (computer
04976 type, OS type, event size).
04977 The cache size has therefore be optimized manually to maximize data throughput.
04978 @param buffer_handle buffer handle obtained via bm_open_buffer()
04979 @param read_size cache size for reading events in bytes, zero for no cache
04980 @param write_size cache size for writing events in bytes, zero for no cache
04981 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
04982 */
04983 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
04984 /*------------------------------------------------------------------*/
04985 {
04986    if (rpc_is_remote())
04987       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
04988 
04989 #ifdef LOCAL_ROUTINES
04990    {
04991       BUFFER *pbuf;
04992 
04993       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04994          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
04995          return BM_INVALID_HANDLE;
04996       }
04997 
04998       if (!_buffer[buffer_handle - 1].attached) {
04999          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05000          return BM_INVALID_HANDLE;
05001       }
05002 
05003       if (read_size < 0 || read_size > 1E6) {
05004          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
05005          return BM_INVALID_PARAM;
05006       }
05007 
05008       if (write_size < 0 || write_size > 1E6) {
05009          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
05010          return BM_INVALID_PARAM;
05011       }
05012 
05013       /* manage read cache */
05014       pbuf = &_buffer[buffer_handle - 1];
05015 
05016       if (pbuf->read_cache_size > 0)
05017          M_FREE(pbuf->read_cache);
05018 
05019       if (read_size > 0) {
05020          pbuf->read_cache = (char *) M_MALLOC(read_size);
05021          if (pbuf->read_cache == NULL) {
05022             cm_msg(MERROR, "bm_set_cache_size",
05023                    "not enough memory to allocate cache buffer");
05024             return BM_NO_MEMORY;
05025          }
05026       }
05027 
05028       pbuf->read_cache_size = read_size;
05029       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05030 
05031       /* manage write cache */
05032       if (pbuf->write_cache_size > 0)
05033          M_FREE(pbuf->write_cache);
05034 
05035       if (write_size > 0) {
05036          pbuf->write_cache = (char *) M_MALLOC(write_size);
05037          if (pbuf->write_cache == NULL) {
05038             cm_msg(MERROR, "bm_set_cache_size",
05039                    "not enough memory to allocate cache buffer");
05040             return BM_NO_MEMORY;
05041          }
05042       }
05043 
05044       pbuf->write_cache_size = write_size;
05045       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05046 
05047    }
05048 #endif                          /* LOCAL_ROUTINES */
05049 
05050    return BM_SUCCESS;
05051 }
05052 
05053 /********************************************************************/
05054 /**
05055 Compose a Midas event header.
05056 An event header can usually be set-up manually or
05057 through this routine. If the data size of the event is not known when
05058 the header is composed, it can be set later with event_header->data-size = <...>
05059 Following structure is created at the beginning of an event
05060 \code
05061 typedef struct {
05062  short int     event_id;
05063  short int     trigger_mask;
05064  DWORD         serial_number;
05065  DWORD         time_stamp;
05066  DWORD         data_size;
05067 } EVENT_HEADER;
05068 
05069 char event[1000];
05070  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05071  *(event+sizeof(EVENT_HEADER)) = <...>
05072 \endcode
05073 @param event_header pointer to the event header
05074 @param event_id event ID of the event
05075 @param trigger_mask trigger mask of the event
05076 @param size size if the data part of the event in bytes
05077 @param serial serial number
05078 @return BM_SUCCESS
05079 */
05080 INT bm_compose_event(EVENT_HEADER * event_header,
05081                      short int event_id, short int trigger_mask, DWORD size, DWORD serial)
05082 {
05083    event_header->event_id = event_id;
05084    event_header->trigger_mask = trigger_mask;
05085    event_header->data_size = size;
05086    event_header->time_stamp = ss_time();
05087    event_header->serial_number = serial;
05088 
05089    return BM_SUCCESS;
05090 }
05091 
05092 
05093 /**dox***************************************************************/
05094 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05095 
05096 /********************************************************************/
05097 INT bm_add_event_request(INT buffer_handle, short int event_id,
05098                          short int trigger_mask,
05099                          INT sampling_type,
05100                          void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *),
05101                          INT request_id)
05102 /********************************************************************\
05103 
05104   Routine:  bm_add_event_request
05105 
05106   Purpose:  Place a request for a specific event type in the client
05107             structure of the buffer refereced by buffer_handle.
05108 
05109   Input:
05110     INT          buffer_handle  Handle to the buffer where the re-
05111                                 quest should be placed in
05112 
05113     short int    event_id       Event ID      \
05114     short int    trigger_mask   Trigger mask  / Event specification
05115 
05116     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05117 
05118 
05119                  Note: to request all types of events, use
05120                    event_id = 0 (all others should be !=0 !)
05121                    trigger_mask = TRIGGER_ALL
05122                    sampling_typ = GET_ALL
05123 
05124 
05125     void         *func          Callback function
05126     INT          request_id     Request id (unique number assigned
05127                                 by bm_request_event)
05128 
05129   Output:
05130     none
05131 
05132   Function value:
05133     BM_SUCCESS              Successful completion
05134     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05135                             MIDAS.H should be increased.
05136     BM_INVALID_HANDLE       Buffer handle is invalid
05137     RPC_NET_ERROR           Network error
05138 
05139 \********************************************************************/
05140 {
05141    if (rpc_is_remote())
05142       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05143                       trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
05144 
05145 #ifdef LOCAL_ROUTINES
05146    {
05147       INT i;
05148       BUFFER_CLIENT *pclient;
05149 
05150       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05151          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05152                 buffer_handle);
05153          return BM_INVALID_HANDLE;
05154       }
05155 
05156       if (!_buffer[buffer_handle - 1].attached) {
05157          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05158                 buffer_handle);
05159          return BM_INVALID_HANDLE;
05160       }
05161 
05162       /* avoid callback/non callback requests */
05163       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05164          cm_msg(MERROR, "bm_add_event_request",
05165                 "mixing callback/non callback requests not possible");
05166          return BM_INVALID_MIXING;
05167       }
05168 
05169       /* get a pointer to the proper client structure */
05170       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05171                   client[_buffer[buffer_handle - 1].client_index]);
05172 
05173       /* lock buffer */
05174       bm_lock_buffer(buffer_handle);
05175 
05176       /* look for a empty request entry */
05177       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05178          if (!pclient->event_request[i].valid)
05179             break;
05180 
05181       if (i == MAX_EVENT_REQUESTS) {
05182          bm_unlock_buffer(buffer_handle);
05183          return BM_NO_MEMORY;
05184       }
05185 
05186       /* setup event_request structure */
05187       pclient->event_request[i].id = request_id;
05188       pclient->event_request[i].valid = TRUE;
05189       pclient->event_request[i].event_id = event_id;
05190       pclient->event_request[i].trigger_mask = trigger_mask;
05191       pclient->event_request[i].sampling_type = sampling_type;
05192       pclient->event_request[i].dispatch = func;
05193 
05194       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05195 
05196       /* set callback flag in buffer structure */
05197       if (func != NULL)
05198          _buffer[buffer_handle - 1].callback = TRUE;
05199 
05200       /*
05201          Save the index of the last request in the list so that later only the
05202          requests 0..max_request_index-1 have to be searched through.
05203        */
05204 
05205       if (i + 1 > pclient->max_request_index)
05206          pclient->max_request_index = i + 1;
05207 
05208       bm_unlock_buffer(buffer_handle);
05209    }
05210 #endif                          /* LOCAL_ROUTINES */
05211 
05212    return BM_SUCCESS;
05213 }
05214 
05215 /**dox***************************************************************/
05216 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05217 
05218 /********************************************************************/
05219 /**
05220 Place an event request based on certain characteristics.
05221 Multiple event requests can be placed for each buffer, which
05222 are later identified by their request ID. They can contain different callback
05223 routines. Example see bm_open_buffer() and bm_receive_event()
05224 @param buffer_handle buffer handle obtained via bm_open_buffer()
05225 @param event_id event ID for requested events. Use EVENTID_ALL
05226 to receive events with any ID.
05227 @param trigger_mask trigger mask for requested events.
05228 The requested events must have at least one bit in its
05229 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05230 receive events with any trigger mask.
05231 @param sampling_type specifies how many events to receive.
05232 A value of GET_ALL receives all events which
05233 match the specified event ID and trigger mask. If the events are consumed slower
05234 than produced, the producer is automatically slowed down. A value of GET_SOME
05235 receives as much events as possible without slowing down the producer. GET_ALL is
05236 typically used by the logger, while GET_SOME is typically used by analyzers.
05237 @param request_id request ID returned by the function.
05238 This ID is passed to the callback routine and must
05239 be used in the bm_delete_request() routine.
05240 @param func allback routine which gets called when an event of the
05241 specified type is received.
05242 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05243 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05244 should be increased.
05245 */
05246 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05247                      short int trigger_mask,
05248                      INT sampling_type, HNDLE * request_id,
05249                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05250 {
05251    INT index, status;
05252 
05253    /* allocate new space for the local request list */
05254    if (_request_list_entries == 0) {
05255       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05256       memset(_request_list, 0, sizeof(REQUEST_LIST));
05257       if (_request_list == NULL) {
05258          cm_msg(MERROR, "bm_request_event",
05259                 "not enough memory to allocate request list buffer");
05260          return BM_NO_MEMORY;
05261       }
05262 
05263       _request_list_entries = 1;
05264       index = 0;
05265    } else {
05266       /* check for a deleted entry */
05267       for (index = 0; index < _request_list_entries; index++)
05268          if (!_request_list[index].buffer_handle)
05269             break;
05270 
05271       /* if not found, create new one */
05272       if (index == _request_list_entries) {
05273          _request_list = (REQUEST_LIST *) realloc(_request_list,
05274                                                   sizeof(REQUEST_LIST) *
05275                                                   (_request_list_entries + 1));
05276          if (_request_list == NULL) {
05277             cm_msg(MERROR, "bm_request_event",
05278                    "not enough memory to allocate request list buffer");
05279             return BM_NO_MEMORY;
05280          }
05281 
05282          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05283 
05284          _request_list_entries++;
05285       }
05286    }
05287 
05288    /* initialize request list */
05289    _request_list[index].buffer_handle = buffer_handle;
05290    _request_list[index].event_id = event_id;
05291    _request_list[index].trigger_mask = trigger_mask;
05292    _request_list[index].dispatcher = func;
05293 
05294    *request_id = index;
05295 
05296    /* add request in buffer structure */
05297    status = bm_add_event_request(buffer_handle, event_id, trigger_mask,
05298                                  sampling_type, func, index);
05299    if (status != BM_SUCCESS)
05300       return status;
05301 
05302    return BM_SUCCESS;
05303 }
05304 
05305 /********************************************************************/
05306 /**
05307 Delete a previously placed request for a specific event
05308 type in the client structure of the buffer refereced by buffer_handle.
05309 @param buffer_handle  Handle to the buffer where the re-
05310                                 quest should be placed in
05311 @param request_id     Request id returned by bm_request_event
05312 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR 
05313 */
05314 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05315 {
05316    if (rpc_is_remote())
05317       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
05318 
05319 #ifdef LOCAL_ROUTINES
05320    {
05321       INT i, deleted;
05322       BUFFER_CLIENT *pclient;
05323 
05324       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05325          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05326                 buffer_handle);
05327          return BM_INVALID_HANDLE;
05328       }
05329 
05330       if (!_buffer[buffer_handle - 1].attached) {
05331          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05332                 buffer_handle);
05333          return BM_INVALID_HANDLE;
05334       }
05335 
05336       /* get a pointer to the proper client structure */
05337       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05338                   client[_buffer[buffer_handle - 1].client_index]);
05339 
05340       /* lock buffer */
05341       bm_lock_buffer(buffer_handle);
05342 
05343       /* check all requests and set to zero if matching */
05344       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
05345          if (pclient->event_request[i].valid &&
05346              pclient->event_request[i].id == request_id) {
05347             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
05348             deleted++;
05349          }
05350 
05351       /* calculate new max_request_index entry */
05352       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
05353          if (pclient->event_request[i].valid)
05354             break;
05355 
05356       pclient->max_request_index = i + 1;
05357 
05358       /* caluclate new all_flag */
05359       pclient->all_flag = FALSE;
05360 
05361       for (i = 0; i < pclient->max_request_index; i++)
05362          if (pclient->event_request[i].valid &&
05363              (pclient->event_request[i].sampling_type & GET_ALL)) {
05364             pclient->all_flag = TRUE;
05365             break;
05366          }
05367 
05368       bm_unlock_buffer(buffer_handle);
05369 
05370       if (!deleted)
05371          return BM_NOT_FOUND;
05372    }
05373 #endif                          /* LOCAL_ROUTINES */
05374 
05375    return BM_SUCCESS;
05376 }
05377 
05378 /********************************************************************/
05379 /** 
05380 Deletes an event request previously done with bm_request_event().
05381 When an event request gets deleted, events of that requested type are
05382 not received any more. When a buffer is closed via bm_close_buffer(), all
05383 event requests from that buffer are deleted automatically
05384 @param request_id request identifier given by bm_request_event()
05385 @return BM_SUCCESS, BM_INVALID_HANDLE
05386 */
05387 INT bm_delete_request(INT request_id)
05388 {
05389    if (request_id < 0 || request_id >= _request_list_entries)
05390       return BM_INVALID_HANDLE;
05391 
05392    /* remove request entry from buffer */
05393    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
05394 
05395    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
05396 
05397    return BM_SUCCESS;
05398 }
05399 
05400 /********************************************************************/
05401 /** 
05402 Sends an event to a buffer.
05403 This function check if the buffer has enough space for the
05404 event, then copies the event to the buffer in shared memory.
05405 If clients have requests for the event, they are notified via an UDP packet.
05406 \code
05407 char event[1000];
05408 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05409 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05410 
05411 // set first byte of event
05412 *(event+sizeof(EVENT_HEADER)) = <...>
05413 #include <stdio.h>
05414 #include "midas.h"
05415 main()
05416 {
05417  INT status, i;
05418  HNDLE hbuf;
05419  char event[1000];
05420  status = cm_connect_experiment("", "Sample", "Producer", NULL);
05421  if (status != CM_SUCCESS)
05422  return 1;
05423  bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
05424 
05425  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05426  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05427 
05428  // set event data
05429  for (i=0 ; i<100 ; i++)
05430  *(event+sizeof(EVENT_HEADER)+i) = i;
05431  // send event
05432  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
05433  cm_disconnect_experiment();
05434  return 0;
05435 }
05436 \endcode
05437 @param buffer_handle Buffer handle obtained via bm_open_buffer()
05438 @param source Address of event buffer
05439 @param buf_size Size of event including event header in bytes
05440 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
05441 blocks if the buffer has not enough free space to receive the event.
05442 If TRUE, the function returns immediately with a
05443 value of BM_ASYNC_RETURN without writing the event to the buffer
05444 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
05445 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
05446 buffer has not enough space to receive event<br>
05447 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
05448 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h and
05449 recompile.
05450 */
05451 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
05452 {
05453    EVENT_HEADER *pevent;
05454 
05455    /* check if event size defined in header matches buf_size */
05456    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
05457                                         sizeof(EVENT_HEADER))) {
05458       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
05459              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
05460                                             sizeof(EVENT_HEADER)));
05461       return BM_INVALID_PARAM;
05462    }
05463 
05464    /* check for maximal event size */
05465    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
05466       cm_msg(MERROR, "bm_send_event",
05467              "event size (%d) larger than maximum event size (%d)",
05468              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
05469       return BM_NO_MEMORY;
05470    }
05471 
05472    if (rpc_is_remote())
05473       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
05474 
05475 #ifdef LOCAL_ROUTINES
05476    {
05477       BUFFER *pbuf;
05478       BUFFER_HEADER *pheader;
05479       BUFFER_CLIENT *pclient, *pc;
05480       EVENT_REQUEST *prequest;
05481       EVENT_HEADER *pevent_test;
05482       INT i, j, min_wp, size, total_size, status;
05483       INT increment;
05484       INT my_client_index;
05485       INT old_write_pointer;
05486       INT old_read_pointer, new_read_pointer;
05487       INT num_requests_client;
05488       char *pdata;
05489       BOOL blocking;
05490       INT n_blocking;
05491       INT request_id;
05492       char str[80];
05493 
05494       pbuf = &_buffer[buffer_handle - 1];
05495 
05496       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05497          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
05498          return BM_INVALID_HANDLE;
05499       }
05500 
05501       if (!pbuf->attached) {
05502          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
05503          return BM_INVALID_HANDLE;
05504       }
05505 
05506       pevent = (EVENT_HEADER *) source;
05507       total_size = buf_size;
05508 
05509       /* round up total_size to next DWORD boundary */
05510       total_size = ALIGN8(total_size);
05511 
05512       /* look if there is space in the cache */
05513       if (pbuf->write_cache_size) {
05514          status = BM_SUCCESS;
05515 
05516          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
05517             status = bm_flush_cache(buffer_handle, async_flag);
05518 
05519          if (status != BM_SUCCESS)
05520             return status;
05521 
05522          if (total_size < pbuf->write_cache_size) {
05523             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
05524 
05525             pbuf->write_cache_wp += total_size;
05526             return BM_SUCCESS;
05527          }
05528       }
05529 
05530       /* calculate some shorthands */
05531       pheader = _buffer[buffer_handle - 1].buffer_header;
05532       pdata = (char *) (pheader + 1);
05533       my_client_index = _buffer[buffer_handle - 1].client_index;
05534       pclient = pheader->client;
05535 
05536       /* check if buffer is large enough */
05537       if (total_size >= pheader->size) {
05538          cm_msg(MERROR, "bm_send_event",
05539                 "total event size (%d) larger than buffer size (%d)", total_size,
05540                 pheader->size);
05541          return BM_NO_MEMORY;
05542       }
05543 
05544       /* lock the buffer */
05545       bm_lock_buffer(buffer_handle);
05546 
05547       /* check if enough space in buffer left */
05548       do {
05549          size = pheader->read_pointer - pheader->write_pointer;
05550          if (size <= 0)
05551             size += pheader->size;
05552 
05553          if (size <= total_size) {      /* note the '<=' to avoid 100% filling */
05554             /* if not enough space, find out who's blocking */
05555             n_blocking = 0;
05556 
05557             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05558                if (pc->pid) {
05559                   if (pc->read_pointer == pheader->read_pointer) {
05560                      /*
05561                         First assume that the client with the "minimum" read pointer
05562                         is not really blocking due to a GET_ALL request.
05563                       */
05564                      blocking = FALSE;
05565                      request_id = -1;
05566 
05567                      /* check if this request blocks */
05568 
05569                      prequest = pc->event_request;
05570                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
05571 
05572                      for (j = 0; j < pc->max_request_index; j++, prequest++)
05573                         if (prequest->valid &&
05574                             bm_match_event(prequest->event_id, prequest->trigger_mask,
05575                                            pevent_test)) {
05576                            request_id = prequest->id;
05577                            if (prequest->sampling_type & GET_ALL) {
05578                               blocking = TRUE;
05579                               break;
05580                            }
05581                         }
05582 
05583                      if (!blocking) {
05584                         /*
05585                            The blocking guy has no GET_ALL request for this event
05586                            -> shift its read pointer.
05587                          */
05588 
05589                         old_read_pointer = pc->read_pointer;
05590 
05591                         increment = sizeof(EVENT_HEADER) +
05592                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
05593 
05594                         /* correct increment for DWORD boundary */
05595                         increment = ALIGN8(increment);
05596 
05597                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
05598 
05599                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05600                            new_read_pointer = 0;
05601 
05602                         pc->read_pointer = new_read_pointer;
05603                      } else {
05604                         n_blocking++;
05605                      }
05606 
05607                      /* wake that client if it has a request */
05608                      if (pc->read_wait && request_id != -1) {
05609 #ifdef DEBUG_MSG
05610                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
05611                                pheader->read_pointer, pheader->write_pointer);
05612 #endif
05613                         sprintf(str, "B %s %d", pheader->name, request_id);
05614                         ss_resume(pc->port, str);
05615                      }
05616 
05617 
05618                   }             /* read_pointer blocks */
05619                }
05620             /* client loop */
05621             if (n_blocking > 0) {
05622                /* at least one client is blocking */
05623 
05624                bm_unlock_buffer(buffer_handle);
05625 
05626                /* return now in ASYNC mode */
05627                if (async_flag)
05628                   return BM_ASYNC_RETURN;
05629 
05630 #ifdef DEBUG_MSG
05631                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
05632                       pheader->read_pointer,
05633                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05634 #endif
05635 
05636                /* has the read pointer moved in between ? */
05637                size = pheader->read_pointer - pheader->write_pointer;
05638                if (size <= 0)
05639                   size += pheader->size;
05640 
05641                /* suspend process */
05642                if (size <= total_size) {
05643                   /* signal other clients wait mode */
05644                   pclient[my_client_index].write_wait = total_size;
05645 
05646                   status = ss_suspend(1000, MSG_BM);
05647 
05648                   pclient[my_client_index].write_wait = 0;
05649 
05650                   /* return if TCP connection broken */
05651                   if (status == SS_ABORT)
05652                      return SS_ABORT;
05653                }
05654 #ifdef DEBUG_MSG
05655                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
05656                       pheader->read_pointer,
05657                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05658 #endif
05659 
05660                bm_lock_buffer(buffer_handle);
05661 
05662                /* has the write pointer moved in between ? */
05663                size = pheader->read_pointer - pheader->write_pointer;
05664                if (size <= 0)
05665                   size += pheader->size;
05666             } else {
05667                /*
05668                   calculate new global read pointer as "minimum" of
05669                   client read pointers
05670                 */
05671                min_wp = pheader->write_pointer;
05672 
05673                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05674                   if (pc->pid) {
05675                      if (pc->read_pointer < min_wp)
05676                         min_wp = pc->read_pointer;
05677 
05678                      if (pc->read_pointer > pheader->write_pointer &&
05679                          pc->read_pointer - pheader->size < min_wp)
05680                         min_wp = pc->read_pointer - pheader->size;
05681                   }
05682 
05683                if (min_wp < 0)
05684                   min_wp += pheader->size;
05685 
05686                pheader->read_pointer = min_wp;
05687             }
05688 
05689          }
05690          /* if (size <= total_size) */
05691       } while (size <= total_size);
05692 
05693       /* we have space, so let's copy the event */
05694       old_write_pointer = pheader->write_pointer;
05695 
05696       if (pheader->write_pointer + total_size <= pheader->size) {
05697          memcpy(pdata + pheader->write_pointer, pevent, total_size);
05698          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
05699          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05700             pheader->write_pointer = 0;
05701       } else {
05702          /* split event */
05703          size = pheader->size - pheader->write_pointer;
05704 
05705          memcpy(pdata + pheader->write_pointer, pevent, size);
05706          memcpy(pdata, (char *) pevent + size, total_size - size);
05707 
05708          pheader->write_pointer = total_size - size;
05709       }
05710 
05711       /* check which clients have a request for this event */
05712       for (i = 0; i < pheader->max_client_index; i++)
05713          if (pclient[i].pid) {
05714             prequest = pclient[i].event_request;
05715             num_requests_client = 0;
05716             request_id = -1;
05717 
05718             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
05719                if (prequest->valid &&
05720                    bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
05721                   if (prequest->sampling_type & GET_ALL)
05722                      pclient[i].num_waiting_events++;
05723 
05724                   num_requests_client++;
05725                   request_id = prequest->id;
05726                }
05727 
05728             /* if that client has a request and is suspended, wake it up */
05729             if (num_requests_client && pclient[i].read_wait) {
05730 #ifdef DEBUG_MSG
05731                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
05732                       pheader->write_pointer);
05733 #endif
05734                sprintf(str, "B %s %d", pheader->name, request_id);
05735                ss_resume(pclient[i].port, str);
05736             }
05737 
05738             /* if that client has no request, shift its read pointer */
05739             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
05740                pclient[i].read_pointer = pheader->write_pointer;
05741          }
05742 
05743       /* shift read pointer of own client */
05744       if (pclient[my_client_index].read_pointer == old_write_pointer)
05745          pclient[my_client_index].read_pointer = pheader->write_pointer;
05746 
05747       /* calculate global read pointer as "minimum" of client read pointers */
05748       min_wp = pheader->write_pointer;
05749 
05750       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05751          if (pc->pid) {
05752             if (pc->read_pointer < min_wp)
05753                min_wp = pc->read_pointer;
05754 
05755             if (pc->read_pointer > pheader->write_pointer &&
05756                 pc->read_pointer - pheader->size < min_wp)
05757                min_wp = pc->read_pointer - pheader->size;
05758          }
05759 
05760       if (min_wp < 0)
05761          min_wp += pheader->size;
05762 
05763 #ifdef DEBUG_MSG
05764       if (min_wp == pheader->read_pointer)
05765          cm_msg(MDEBUG, "bm_send_event -> wp=%d", pheader->write_pointer);
05766       else
05767          cm_msg(MDEBUG, "bm_send_event -> wp=%d, rp %d -> %d, size=%d",
05768                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
05769 #endif
05770 
05771       pheader->read_pointer = min_wp;
05772 
05773       /* update statistics */
05774       pheader->num_in_events++;
05775 
05776       /* unlock the buffer */
05777       bm_unlock_buffer(buffer_handle);
05778    }
05779 #endif                          /* LOCAL_ROUTINES */
05780 
05781    return BM_SUCCESS;
05782 }
05783 
05784 /********************************************************************/
05785 /**
05786 Empty write cache.
05787 This function should be used if events in the write cache
05788 should be visible to the consumers immediately. It should be called at the
05789 end of each run, otherwise events could be kept in the write buffer and will
05790 flow to the data of the next run.
05791 @param buffer_handle Buffer handle obtained via bm_open_buffer()
05792 @param async_flag Synchronous/asynchronous flag.
05793 If FALSE, the function blocks if the buffer has not
05794 enough free space to receive the full cache. If TRUE, the function returns
05795 immediately with a value of BM_ASYNC_RETURN without writing the cache.
05796 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
05797 BM_ASYNC_RETURN Routine called with async_flag == TRUE
05798 and buffer has not enough space to receive cache<br>
05799 BM_NO_MEMORY Event is too large for network buffer or event buffer.
05800 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h
05801 and recompile.
05802 */
05803 INT bm_flush_cache(INT buffer_handle, INT async_flag)
05804 {
05805    if (rpc_is_remote())
05806       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
05807 
05808 #ifdef LOCAL_ROUTINES
05809    {
05810       BUFFER *pbuf;
05811       BUFFER_HEADER *pheader;
05812       BUFFER_CLIENT *pclient, *pc;
05813       EVENT_REQUEST *prequest;
05814       EVENT_HEADER *pevent, *pevent_test;
05815       INT i, j, min_wp, size, total_size, status;
05816       INT increment;
05817       INT my_client_index;
05818       INT old_write_pointer;
05819       INT old_read_pointer, new_read_pointer;
05820       char *pdata;
05821       BOOL blocking;
05822       INT n_blocking;
05823       INT request_id;
05824       char str[80];
05825 
05826       pbuf = &_buffer[buffer_handle - 1];
05827 
05828       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05829          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
05830          return BM_INVALID_HANDLE;
05831       }
05832 
05833       if (!pbuf->attached) {
05834          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
05835          return BM_INVALID_HANDLE;
05836       }
05837 
05838       if (pbuf->write_cache_size == 0)
05839          return BM_SUCCESS;
05840 
05841       /* check if anything needs to be flushed */
05842       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
05843          return BM_SUCCESS;
05844 
05845       /* calculate some shorthands */
05846       pheader = _buffer[buffer_handle - 1].buffer_header;
05847       pdata = (char *) (pheader + 1);
05848       my_client_index = _buffer[buffer_handle - 1].client_index;
05849       pclient = pheader->client;
05850       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
05851 
05852       /* lock the buffer */
05853       bm_lock_buffer(buffer_handle);
05854 
05855 #ifdef DEBUG_MSG
05856       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer,
05857              pheader->write_pointer);
05858 #endif
05859 
05860       /* check if enough space in buffer left */
05861       do {
05862          size = pheader->read_pointer - pheader->write_pointer;
05863          if (size <= 0)
05864             size += pheader->size;
05865 
05866          if (size <= pbuf->write_cache_wp) {
05867             /* if not enough space, find out who's blocking */
05868             n_blocking = 0;
05869 
05870             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05871                if (pc->pid) {
05872                   if (pc->read_pointer == pheader->read_pointer) {
05873                      /*
05874                         First assume that the client with the "minimum" read pointer
05875                         is not really blocking due to a GET_ALL request.
05876                       */
05877                      blocking = FALSE;
05878                      request_id = -1;
05879 
05880                      /* check if this request blocks. */
05881                      prequest = pc->event_request;
05882                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
05883 
05884                      for (j = 0; j < pc->max_request_index; j++, prequest++)
05885                         if (prequest->valid &&
05886                             bm_match_event(prequest->event_id, prequest->trigger_mask,
05887                                            pevent_test)) {
05888                            request_id = prequest->id;
05889                            if (prequest->sampling_type & GET_ALL) {
05890                               blocking = TRUE;
05891                               break;
05892                            }
05893                         }
05894 
05895                      if (!blocking) {
05896                         /*
05897                            The blocking guy has no GET_ALL request for this event
05898                            -> shift its read pointer.
05899                          */
05900 
05901                         old_read_pointer = pc->read_pointer;
05902 
05903                         increment = sizeof(EVENT_HEADER) +
05904                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
05905 
05906                         /* correct increment for DWORD boundary */
05907                         increment = ALIGN8(increment);
05908 
05909                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
05910 
05911                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05912                            new_read_pointer = 0;
05913 
05914 #ifdef DEBUG_MSG
05915                         cm_msg(MDEBUG, "bm_flush_cache: shift client %d rp=%d -> =%d", i,
05916                                pc->read_pointer, new_read_pointer);
05917 #endif
05918 
05919                         pc->read_pointer = new_read_pointer;
05920                      } else {
05921                         n_blocking++;
05922                      }
05923 
05924                      /* wake that client if it has a request */
05925                      if (pc->read_wait && request_id != -1) {
05926 #ifdef DEBUG_MSG
05927                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
05928                                pheader->read_pointer, pheader->write_pointer);
05929 #endif
05930                         sprintf(str, "B %s %d", pheader->name, request_id);
05931                         ss_resume(pc->port, str);
05932                      }
05933 
05934 
05935                   }             /* read_pointer blocks */
05936                }
05937             /* client loop */
05938             if (n_blocking > 0) {
05939                /* at least one client is blocking */
05940 
05941                bm_unlock_buffer(buffer_handle);
05942 
05943                /* return now in ASYNC mode */
05944                if (async_flag)
05945                   return BM_ASYNC_RETURN;
05946 
05947 #ifdef DEBUG_MSG
05948                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
05949                       pheader->read_pointer,
05950                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05951 #endif
05952 
05953                /* has the read pointer moved in between ? */
05954                size = pheader->read_pointer - pheader->write_pointer;
05955                if (size <= 0)
05956                   size += pheader->size;
05957 
05958                /* suspend process */
05959                if (size <= pbuf->write_cache_wp) {
05960                   /* signal other clients wait mode */
05961                   pclient[my_client_index].write_wait = pbuf->write_cache_wp;
05962 
05963                   status = ss_suspend(1000, MSG_BM);
05964 
05965                   pclient[my_client_index].write_wait = 0;
05966 
05967                   /* return if TCP connection broken */
05968                   if (status == SS_ABORT)
05969                      return SS_ABORT;
05970                }
05971 #ifdef DEBUG_MSG
05972                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
05973                       pheader->read_pointer,
05974                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05975 #endif
05976 
05977                bm_lock_buffer(buffer_handle);
05978 
05979                /* has the write pointer moved in between ? */
05980                size = pheader->read_pointer - pheader->write_pointer;
05981                if (size <= 0)
05982                   size += pheader->size;
05983             } else {
05984                /*
05985                   calculate new global read pointer as "minimum" of
05986                   client read pointers
05987                 */
05988                min_wp = pheader->write_pointer;
05989 
05990                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05991                   if (pc->pid) {
05992                      if (pc->read_pointer < min_wp)
05993                         min_wp = pc->read_pointer;
05994 
05995                      if (pc->read_pointer > pheader->write_pointer &&
05996                          pc->read_pointer - pheader->size < min_wp)
05997                         min_wp = pc->read_pointer - pheader->size;
05998                   }
05999 
06000                if (min_wp < 0)
06001                   min_wp += pheader->size;
06002 
06003                pheader->read_pointer = min_wp;
06004             }
06005 
06006          }
06007          /* if (size <= total_size) */
06008       } while (size <= pbuf->write_cache_wp);
06009 
06010 
06011       /* we have space, so let's copy the event */
06012       old_write_pointer = pheader->write_pointer;
06013 
06014 #ifdef DEBUG_MSG
06015       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
06016              pheader->write_pointer);
06017 #endif
06018 
06019       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06020          /* loop over all events in cache */
06021 
06022          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06023          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06024 
06025          /* correct size for DWORD boundary */
06026          total_size = ALIGN8(total_size);
06027 
06028          if (pheader->write_pointer + total_size <= pheader->size) {
06029             memcpy(pdata + pheader->write_pointer, pevent, total_size);
06030             pheader->write_pointer = (pheader->write_pointer + total_size) %
06031                 pheader->size;
06032             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06033                pheader->write_pointer = 0;
06034          } else {
06035             /* split event */
06036             size = pheader->size - pheader->write_pointer;
06037 
06038             memcpy(pdata + pheader->write_pointer, pevent, size);
06039             memcpy(pdata, (char *) pevent + size, total_size - size);
06040 
06041             pheader->write_pointer = total_size - size;
06042          }
06043 
06044          pbuf->write_cache_rp += total_size;
06045       }
06046 
06047       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06048 
06049       /* check which clients are waiting */
06050       for (i = 0; i < pheader->max_client_index; i++)
06051          if (pclient[i].pid && pclient[i].read_wait) {
06052 #ifdef DEBUG_MSG
06053             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06054                    pheader->write_pointer);
06055 #endif
06056             sprintf(str, "B %s %d", pheader->name, -1);
06057             ss_resume(pclient[i].port, str);
06058          }
06059 
06060       /* shift read pointer of own client */
06061       if (pclient[my_client_index].read_pointer == old_write_pointer)
06062          pclient[my_client_index].read_pointer = pheader->write_pointer;
06063 
06064       /* calculate global read pointer as "minimum" of client read pointers */
06065       min_wp = pheader->write_pointer;
06066 
06067       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06068          if (pc->pid) {
06069 #ifdef DEBUG_MSG
06070             cm_msg(MDEBUG, "bm_flush_cache: client %d rp=%d", i, pc->read_pointer);
06071 #endif
06072             if (pc->read_pointer < min_wp)
06073                min_wp = pc->read_pointer;
06074 
06075             if (pc->read_pointer > pheader->write_pointer &&
06076                 pc->read_pointer - pheader->size < min_wp)
06077                min_wp = pc->read_pointer - pheader->size;
06078          }
06079 
06080       if (min_wp < 0)
06081          min_wp += pheader->size;
06082 
06083 #ifdef DEBUG_MSG
06084       if (min_wp == pheader->read_pointer)
06085          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d", pheader->write_pointer);
06086       else
06087          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d, rp %d -> %d, size=%d",
06088                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06089 #endif
06090 
06091       pheader->read_pointer = min_wp;
06092 
06093       /* update statistics */
06094       pheader->num_in_events++;
06095 
06096       /* unlock the buffer */
06097       bm_unlock_buffer(buffer_handle);
06098    }
06099 #endif                          /* LOCAL_ROUTINES */
06100 
06101    return BM_SUCCESS;
06102 }
06103 
06104 /********************************************************************/
06105 /**
06106 Receives events directly.
06107 This function is an alternative way to receive events without
06108 a main loop.
06109 
06110 It can be used in analysis systems which actively receive events,
06111 rather than using callbacks. A analysis package could for example contain its own
06112 command line interface. A command
06113 like "receive 1000 events" could make it necessary to call bm_receive_event()
06114 1000 times in a row to receive these events and then return back to the
06115 command line prompt.
06116 The according bm_request_event() call contains NULL as the
06117 callback routine to indicate that bm_receive_event() is called to receive
06118 events.
06119 \code
06120 #include <stdio.h>
06121 #include "midas.h"
06122 void process_event(EVENT_HEADER *pheader)
06123 {
06124  printf("Received event #%d\r",
06125  pheader->serial_number);
06126 }
06127 main()
06128 {
06129   INT status, request_id;
06130   HNDLE hbuf;
06131   char event_buffer[1000];
06132   status = cm_connect_experiment("", "Sample",
06133   "Simple Analyzer", NULL);
06134   if (status != CM_SUCCESS)
06135    return 1;
06136   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06137   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06138 
06139   do
06140   {
06141    size = sizeof(event_buffer);
06142    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06143   if (status == CM_SUCCESS)
06144    process_event((EVENT_HEADER *) event_buffer);
06145    <...do something else...>
06146    status = cm_yield(0);
06147   } while (status != RPC_SHUTDOWN &&
06148   status != SS_ABORT);
06149   cm_disconnect_experiment();
06150   return 0;
06151 }
06152 \endcode
06153 @param buffer_handle buffer handle
06154 @param destination destination address where event is written to
06155 @param buf_size size of destination buffer on input, size of event plus
06156 header on return.
06157 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06158 blocks if no event is available. If TRUE, the function returns immediately
06159 with a value of BM_ASYNC_RETURN without receiving any event.
06160 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06161 BM_TRUNCATED   The event is larger than the destination buffer and was
06162                therefore truncated <br>
06163 BM_ASYNC_RETURN No event available
06164 */
06165 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06166 {
06167    if (rpc_is_remote()) {
06168       if (*buf_size > NET_BUFFER_SIZE) {
06169          cm_msg(MERROR, "bm_receive_event",
06170                 "max. event size larger than NET_BUFFER_SIZE");
06171          return RPC_NET_ERROR;
06172       }
06173 
06174       return rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle,
06175                       destination, buf_size, async_flag);
06176    }
06177 #ifdef LOCAL_ROUTINES
06178    {
06179       BUFFER *pbuf;
06180       BUFFER_HEADER *pheader;
06181       BUFFER_CLIENT *pclient, *pc, *pctmp;
06182       EVENT_REQUEST *prequest;
06183       EVENT_HEADER *pevent;
06184       char *pdata;
06185       INT convert_flags;
06186       INT i, min_wp, size, max_size, total_size, status = 0;
06187       INT my_client_index;
06188       BOOL found;
06189       INT old_read_pointer, new_read_pointer;
06190 
06191       pbuf = &_buffer[buffer_handle - 1];
06192 
06193       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06194          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06195          return BM_INVALID_HANDLE;
06196       }
06197 
06198       if (!pbuf->attached) {
06199          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06200          return BM_INVALID_HANDLE;
06201       }
06202 
06203       max_size = *buf_size;
06204       *buf_size = 0;
06205 
06206       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06207          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06208       else
06209          convert_flags = 0;
06210 
06211     CACHE_READ:
06212 
06213       /* look if there is anything in the cache */
06214       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06215          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06216          size = pevent->data_size + sizeof(EVENT_HEADER);
06217 
06218          if (size > max_size) {
06219             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, max_size);
06220             cm_msg(MERROR, "bm_receive_event", "event size larger than buffer size");
06221             *buf_size = max_size;
06222             status = BM_TRUNCATED;
06223          } else {
06224             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, size);
06225             *buf_size = size;
06226             status = BM_SUCCESS;
06227          }
06228 
06229          /* now convert event header */
06230          if (convert_flags) {
06231             pevent = (EVENT_HEADER *) destination;
06232             rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
06233             rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06234                                convert_flags);
06235             rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06236                                convert_flags);
06237             rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06238                                convert_flags);
06239             rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06240                                convert_flags);
06241          }
06242 
06243          /* correct size for DWORD boundary */
06244          size = ALIGN8(size);
06245 
06246          pbuf->read_cache_rp += size;
06247 
06248          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06249             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06250 
06251          return status;
06252       }
06253 
06254       /* calculate some shorthands */
06255       pheader = pbuf->buffer_header;
06256       pdata = (char *) (pheader + 1);
06257       my_client_index = pbuf->client_index;
06258       pclient = pheader->client;
06259       pc = pheader->client + my_client_index;
06260 
06261       /* first do a quick check without locking the buffer */
06262       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06263          return BM_ASYNC_RETURN;
06264 
06265       /* lock the buffer */
06266       bm_lock_buffer(buffer_handle);
06267 
06268     LOOP:
06269 
06270       while (pheader->write_pointer == pc->read_pointer) {
06271          bm_unlock_buffer(buffer_handle);
06272 
06273          /* return now in ASYNC mode */
06274          if (async_flag == ASYNC)
06275             return BM_ASYNC_RETURN;
06276 
06277          pc->read_wait = TRUE;
06278 
06279          /* check again pointers (may have moved in between) */
06280          if (pheader->write_pointer == pc->read_pointer) {
06281 #ifdef DEBUG_MSG
06282             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06283                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06284 #endif
06285 
06286             status = ss_suspend(1000, MSG_BM);
06287 
06288 #ifdef DEBUG_MSG
06289             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d",
06290                    pheader->read_pointer, pheader->write_pointer);
06291 #endif
06292 
06293             /* return if TCP connection broken */
06294             if (status == SS_ABORT)
06295                return SS_ABORT;
06296          }
06297 
06298          pc->read_wait = FALSE;
06299 
06300          bm_lock_buffer(buffer_handle);
06301       }
06302 
06303       /* check if event at current read pointer matches a request */
06304       found = FALSE;
06305 
06306       do {
06307          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06308 
06309          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06310          total_size = ALIGN8(total_size);
06311 
06312          prequest = pc->event_request;
06313 
06314          for (i = 0; i < pc->max_request_index; i++, prequest++)
06315             if (prequest->valid &&
06316                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06317                /* we found one, so copy it */
06318 
06319                if (pbuf->read_cache_size > 0 &&
06320                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06321                   if (pbuf->read_cache_size - pbuf->read_cache_wp < total_size)
06322                      goto CACHE_FULL;
06323 
06324                   if (pc->read_pointer + total_size <= pheader->size) {
06325                      /* copy event to cache */
06326                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06327                   } else {
06328                      /* event is splitted */
06329                      size = pheader->size - pc->read_pointer;
06330                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06331                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
06332                             pdata, total_size - size);
06333                   }
06334                } else {
06335                   if (pc->read_pointer + total_size <= pheader->size) {
06336                      /* event is not splitted */
06337                      if (total_size > max_size)
06338                         memcpy(destination, pevent, max_size);
06339                      else
06340                         memcpy(destination, pevent, total_size);
06341                   } else {
06342                      /* event is splitted */
06343                      size = pheader->size - pc->read_pointer;
06344 
06345                      if (size > max_size)
06346                         memcpy(destination, pevent, max_size);
06347                      else
06348                         memcpy(destination, pevent, size);
06349 
06350                      if (total_size > max_size) {
06351                         if (size <= max_size)
06352                            memcpy((char *) destination + size, pdata, max_size - size);
06353                      } else
06354                         memcpy((char *) destination + size, pdata, total_size - size);
06355                   }
06356 
06357                   if (total_size < max_size)
06358                      *buf_size = total_size;
06359                   else
06360                      *buf_size = max_size;
06361 
06362                   /* now convert event header */
06363                   if (convert_flags) {
06364                      pevent = (EVENT_HEADER *) destination;
06365                      rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING,
06366                                         convert_flags);
06367                      rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06368                                         convert_flags);
06369                      rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06370                                         convert_flags);
06371                      rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06372                                         convert_flags);
06373                      rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06374                                         convert_flags);
06375                   }
06376                }
06377 
06378                if (pbuf->read_cache_size > 0 &&
06379                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06380                   pbuf->read_cache_wp += total_size;
06381                } else {
06382                   if (total_size > max_size) {
06383                      cm_msg(MERROR, "bm_receive_event",
06384                             "event size larger than buffer size");
06385                      status = BM_TRUNCATED;
06386                   } else
06387                      status = BM_SUCCESS;
06388                }
06389 
06390                /* update statistics */
06391                found = TRUE;
06392                pheader->num_out_events++;
06393                break;
06394             }
06395 
06396          old_read_pointer = pc->read_pointer;
06397 
06398          /* shift read pointer */
06399          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
06400 
06401          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06402             new_read_pointer = 0;
06403 
06404 #ifdef DEBUG_MSG
06405          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06406                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
06407                 total_size);
06408 #endif
06409 
06410          pc->read_pointer = new_read_pointer;
06411 
06412          /*
06413             Repeat until a requested event is found or no more events
06414             are available.
06415           */
06416 
06417          if (pbuf->read_cache_size == 0 && found)
06418             break;
06419 
06420          /* break if event has bypassed read cache */
06421          if (pbuf->read_cache_size > 0 &&
06422              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
06423             break;
06424 
06425       } while (pheader->write_pointer != pc->read_pointer);
06426 
06427     CACHE_FULL:
06428 
06429       /* calculate global read pointer as "minimum" of client read pointers */
06430       min_wp = pheader->write_pointer;
06431 
06432       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06433          if (pctmp->pid) {
06434             if (pctmp->read_pointer < min_wp)
06435                min_wp = pctmp->read_pointer;
06436 
06437             if (pctmp->read_pointer > pheader->write_pointer &&
06438                 pctmp->read_pointer - pheader->size < min_wp)
06439                min_wp = pctmp->read_pointer - pheader->size;
06440          }
06441 
06442       if (min_wp < 0)
06443          min_wp += pheader->size;
06444 
06445       pheader->read_pointer = min_wp;
06446 
06447       /*
06448          If read pointer has been changed, it may have freed up some space
06449          for waiting producers. So check if free space is now more than 50%
06450          of the buffer size and wake waiting producers.
06451        */
06452       size = pc->read_pointer - pheader->write_pointer;
06453       if (size <= 0)
06454          size += pheader->size;
06455 
06456       if (size >= pheader->size * 0.5)
06457          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06458             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
06459                                                              (pctmp->pid == ss_getpid()
06460                                                               && pctmp->tid !=
06461                                                               ss_gettid()))) {
06462 #ifdef DEBUG_MSG
06463                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
06464                       pheader->read_pointer,
06465                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06466 #endif
06467                ss_resume(pctmp->port, "B  ");
06468             }
06469 
06470       /* if no matching event found, start again */
06471       if (!found)
06472          goto LOOP;
06473 
06474       bm_unlock_buffer(buffer_handle);
06475 
06476       if (pbuf->read_cache_size > 0 &&
06477           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
06478          goto CACHE_READ;
06479 
06480       return status;
06481    }
06482 #else                           /* LOCAL_ROUTINES */
06483 
06484    return SS_SUCCESS;
06485 #endif
06486 }
06487 
06488 /********************************************************************/
06489 /**
06490 Skip all events in current buffer.
06491 
06492 Useful for single event displays to see the newest events
06493 @param buffer_handle      Handle of the buffer. Must be obtained
06494                           via bm_open_buffer.
06495 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR 
06496 */
06497 INT bm_skip_event(INT buffer_handle)
06498 {
06499    if (rpc_is_remote())
06500       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
06501 
06502 #ifdef LOCAL_ROUTINES
06503    {
06504       BUFFER *pbuf;
06505       BUFFER_HEADER *pheader;
06506       BUFFER_CLIENT *pclient;
06507 
06508       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06509          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06510          return BM_INVALID_HANDLE;
06511       }
06512 
06513       pbuf = &_buffer[buffer_handle - 1];
06514       pheader = pbuf->buffer_header;
06515 
06516       if (!pbuf->attached) {
06517          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06518          return BM_INVALID_HANDLE;
06519       }
06520 
06521       /* clear cache */
06522       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
06523          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06524 
06525       bm_lock_buffer(buffer_handle);
06526 
06527       /* forward read pointer to global write pointer */
06528       pclient = pheader->client + pbuf->client_index;
06529       pclient->read_pointer = pheader->write_pointer;
06530 
06531       bm_unlock_buffer(buffer_handle);
06532    }
06533 #endif
06534 
06535    return BM_SUCCESS;
06536 }
06537 
06538 /********************************************************************/
06539 /**
06540 Check a buffer if an event is available and call the dispatch function if found.
06541 @param buffer_name       Name of buffer
06542 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, 
06543                     RPC_NET_ERROR
06544 */
06545 INT bm_push_event(char *buffer_name)
06546 {
06547 #ifdef LOCAL_ROUTINES
06548    {
06549       BUFFER *pbuf;
06550       BUFFER_HEADER *pheader;
06551       BUFFER_CLIENT *pclient, *pc, *pctmp;
06552       EVENT_REQUEST *prequest;
06553       EVENT_HEADER *pevent;
06554       char *pdata;
06555       INT i, min_wp, size, total_size, buffer_handle;
06556       INT my_client_index;
06557       BOOL found;
06558       INT old_read_pointer, new_read_pointer;
06559 
06560       for (i = 0; i < _buffer_entries; i++)
06561          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
06562             break;
06563       if (i == _buffer_entries)
06564          return BM_INVALID_HANDLE;
06565 
06566       buffer_handle = i + 1;
06567       pbuf = &_buffer[buffer_handle - 1];
06568 
06569       if (!pbuf->attached)
06570          return BM_INVALID_HANDLE;
06571 
06572       /* return immediately if no callback routine is defined */
06573       if (!pbuf->callback)
06574          return BM_SUCCESS;
06575 
06576       if (_event_buffer_size == 0) {
06577          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
06578          if (_event_buffer == NULL) {
06579             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
06580             return BM_NO_MEMORY;
06581          }
06582          _event_buffer_size = 1000;
06583       }
06584 
06585     CACHE_READ:
06586 
06587       /* look if there is anything in the cache */
06588       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06589          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06590          size = pevent->data_size + sizeof(EVENT_HEADER);
06591 
06592          /* correct size for DWORD boundary */
06593          size = ALIGN8(size);
06594 
06595          /* increment read pointer */
06596          pbuf->read_cache_rp += size;
06597          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06598             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06599 
06600          /* call dispatcher */
06601          for (i = 0; i < _request_list_entries; i++)
06602             if (_request_list[i].buffer_handle == buffer_handle &&
06603                 bm_match_event(_request_list[i].event_id,
06604                                _request_list[i].trigger_mask, pevent)) {
06605                /* if event is fragmented, call defragmenter */
06606                if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
06607                    (pevent->event_id & 0xF000) == EVENTID_FRAG)
06608                   bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1),
06609                                       _request_list[i].dispatcher);
06610                else
06611                   _request_list[i].dispatcher(buffer_handle, i, pevent,
06612                                               (void *) (pevent + 1));
06613             }
06614 
06615          return BM_MORE_EVENTS;
06616       }
06617 
06618       /* calculate some shorthands */
06619       pheader = pbuf->buffer_header;
06620       pdata = (char *) (pheader + 1);
06621       my_client_index = pbuf->client_index;
06622       pclient = pheader->client;
06623       pc = pheader->client + my_client_index;
06624 
06625       /* first do a quick check without locking the buffer */
06626       if (pheader->write_pointer == pc->read_pointer)
06627          return BM_SUCCESS;
06628 
06629       /* lock the buffer */
06630       bm_lock_buffer(buffer_handle);
06631 
06632     LOOP:
06633 
06634       if (pheader->write_pointer == pc->read_pointer) {
06635          bm_unlock_buffer(buffer_handle);
06636 
06637          /* return if no event available */
06638          return BM_SUCCESS;
06639       }
06640 
06641       /* check if event at current read pointer matches a request */
06642       found = FALSE;
06643 
06644       do {
06645          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06646 
06647          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06648          total_size = ALIGN8(total_size);
06649 
06650          prequest = pc->event_request;
06651 
06652          for (i = 0; i < pc->max_request_index; i++, prequest++)
06653             if (prequest->valid &&
06654                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06655                /* we found one, so copy it */
06656 
06657                if (pbuf->read_cache_size > 0 &&
06658                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06659                   /* copy dispatch function and event to cache */
06660 
06661                   if (pbuf->read_cache_size - pbuf->read_cache_wp <
06662                       total_size + (INT) sizeof(void *) + (INT) sizeof(INT))
06663                      goto CACHE_FULL;
06664 
06665                   if (pc->read_pointer + total_size <= pheader->size) {
06666                      /* copy event to cache */
06667                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06668                   } else {
06669                      /* event is splitted */
06670 
06671                      size = pheader->size - pc->read_pointer;
06672                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06673                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
06674                             pdata, total_size - size);
06675                   }
06676 
06677                   pbuf->read_cache_wp += total_size;
06678                } else {
06679                   /* copy event to copy buffer, save dispatcher */
06680 
06681                   if (total_size > _event_buffer_size) {
06682                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
06683                      _event_buffer_size = total_size;
06684                   }
06685 
06686                   if (pc->read_pointer + total_size <= pheader->size) {
06687                      memcpy(_event_buffer, pevent, total_size);
06688                   } else {
06689                      /* event is splitted */
06690                      size = pheader->size - pc->read_pointer;
06691 
06692                      memcpy(_event_buffer, pevent, size);
06693                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
06694                   }
06695                }
06696 
06697                /* update statistics */
06698                found = TRUE;
06699                pheader->num_out_events++;
06700                break;
06701             }
06702 
06703          old_read_pointer = pc->read_pointer;
06704 
06705          /* shift read pointer */
06706          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
06707 
06708          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06709             new_read_pointer = 0;
06710 
06711 #ifdef DEBUG_MSG
06712          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06713                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
06714                 total_size);
06715 #endif
06716 
06717          pc->read_pointer = new_read_pointer;
06718 
06719          /*
06720             Repeat until a requested event is found or no more events
06721             are available or large event received.
06722           */
06723 
06724          if (pbuf->read_cache_size == 0 && found)
06725             break;
06726 
06727          /* break if event has bypassed read cache */
06728          if (pbuf->read_cache_size > 0 &&
06729              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
06730             break;
06731 
06732       } while (pheader->write_pointer != pc->read_pointer);
06733 
06734     CACHE_FULL:
06735 
06736       /* calculate global read pointer as "minimum" of client read pointers */
06737       min_wp = pheader->write_pointer;
06738 
06739       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06740          if (pctmp->pid) {
06741             if (pctmp->read_pointer < min_wp)
06742                min_wp = pctmp->read_pointer;
06743 
06744             if (pctmp->read_pointer > pheader->write_pointer &&
06745                 pctmp->read_pointer - pheader->size < min_wp)
06746                min_wp = pctmp->read_pointer - pheader->size;
06747          }
06748 
06749       if (min_wp < 0)
06750          min_wp += pheader->size;
06751 
06752       pheader->read_pointer = min_wp;
06753 
06754       /*
06755          If read pointer has been changed, it may have freed up some space
06756          for waiting producers. So check if free space is now more than 50%
06757          of the buffer size and wake waiting producers.
06758        */
06759       size = pc->read_pointer - pheader->write_pointer;
06760       if (size <= 0)
06761          size += pheader->size;
06762 
06763       if (size >= pheader->size * 0.5)
06764          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06765             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
06766                                                              (pctmp->pid == ss_getpid()
06767                                                               && pctmp->tid !=
06768                                                               ss_gettid()))) {
06769 #ifdef DEBUG_MSG
06770                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
06771                       pheader->read_pointer,
06772                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06773 #endif
06774                ss_resume(pctmp->port, "B  ");
06775             }
06776 
06777       /* if no matching event found, start again */
06778       if (!found)
06779          goto LOOP;
06780 
06781       bm_unlock_buffer(buffer_handle);
06782 
06783       if (pbuf->read_cache_size > 0 &&
06784           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
06785          goto CACHE_READ;
06786 
06787       /* call dispatcher */
06788       for (i = 0; i < _request_list_entries; i++)
06789          if (_request_list[i].buffer_handle == buffer_handle &&
06790              bm_match_event(_request_list[i].event_id,
06791                             _request_list[i].trigger_mask, _event_buffer)) {
06792             if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
06793                 (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
06794                bm_defragment_event(buffer_handle, i, _event_buffer,
06795                                    (void *) (((EVENT_HEADER *) _event_buffer) + 1),
06796                                    _request_list[i].dispatcher);
06797             else
06798                _request_list[i].dispatcher(buffer_handle, i, _event_buffer,
06799                                            (void *) (((EVENT_HEADER *) _event_buffer) +
06800                                                      1));
06801          }
06802 
06803       return BM_MORE_EVENTS;
06804    }
06805 #else                           /* LOCAL_ROUTINES */
06806 
06807    return BM_SUCCESS;
06808 #endif
06809 }
06810 
06811 /********************************************************************/
06812 /**
06813 Check if any requested event is waiting in a buffer
06814 @return TRUE             More events are waiting<br>
06815         FALSE            No more events are waiting
06816 */
06817 INT bm_check_buffers()
06818 {
06819 #ifdef LOCAL_ROUTINES
06820    {
06821       INT index, status = 0;
06822       INT server_type, server_conn, tid;
06823       BOOL bMore;
06824       DWORD start_time;
06825 
06826       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
06827       server_conn = rpc_get_server_acception();
06828       tid = ss_gettid();
06829 
06830       /* if running as a server, buffer checking is done by client
06831          via ASYNC bm_receive_event */
06832       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
06833          return FALSE;
06834 
06835       bMore = FALSE;
06836       start_time = ss_millitime();
06837 
06838       /* go through all buffers */
06839       for (index = 0; index < _buffer_entries; index++) {
06840          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
06841             continue;
06842 
06843          if (server_type != ST_SINGLE && _buffer[index].index != tid)
06844             continue;
06845 
06846          if (!_buffer[index].attached)
06847             continue;
06848 
06849          do {
06850 
06851             /* one bm_push_event could cause a run stop and a buffer close, which
06852                would crash the next call to bm_push_event(). So check for valid
06853                buffer on each call */
06854             if (index < _buffer_entries && _buffer[index].buffer_header->name != NULL)
06855                status = bm_push_event(_buffer[index].buffer_header->name);
06856 
06857             if (status != BM_MORE_EVENTS)
06858                break;
06859 
06860             /* stop after one second */
06861             if (ss_millitime() - start_time > 1000) {
06862                bMore = TRUE;
06863                break;
06864             }
06865 
06866          } while (TRUE);
06867       }
06868 
06869       return bMore;
06870 
06871    }
06872 #else                           /* LOCAL_ROUTINES */
06873 
06874    return FALSE;
06875 
06876 #endif
06877 }
06878 
06879 /**dox***************************************************************/
06880 #ifndef DOXYGEN_SHOULD_SKIP_THIS
06881 
06882 /********************************************************************/
06883 INT bm_mark_read_waiting(BOOL flag)
06884 /********************************************************************\
06885 
06886   Routine: bm_mark_read_waiting
06887 
06888   Purpose: Mark all open buffers ready for receiving events.
06889            Called internally by ss_suspend
06890 
06891 
06892   Input:
06893     BOOL flag               TRUE for waiting, FALSE for not waiting
06894 
06895   Output:
06896     none
06897 
06898   Function value:
06899     BM_SUCCESS              Successful completion
06900 
06901 \********************************************************************/
06902 {
06903    if (rpc_is_remote())
06904       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
06905 
06906 #ifdef LOCAL_ROUTINES
06907    {
06908       INT i;
06909       BUFFER_HEADER *pheader;
06910       BUFFER_CLIENT *pclient;
06911 
06912       /* Mark all buffer for read waiting */
06913       for (i = 0; i < _buffer_entries; i++) {
06914          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
06915              _buffer[i].index != rpc_get_server_acception())
06916             continue;
06917 
06918          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
06919              _buffer[i].index != ss_gettid())
06920             continue;
06921 
06922          if (!_buffer[i].attached)
06923             continue;
06924 
06925          pheader = _buffer[i].buffer_header;
06926          pclient = pheader->client + _buffer[i].client_index;
06927          pclient->read_wait = flag;
06928       }
06929    }
06930 #endif                          /* LOCAL_ROUTINES */
06931 
06932    return BM_SUCCESS;
06933 }
06934 
06935 /********************************************************************/
06936 INT bm_notify_client(char *buffer_name, int socket)
06937 /********************************************************************\
06938 
06939   Routine: bm_notify_client
06940 
06941   Purpose: Called by cm_dispatch_ipc. Send an event notification to
06942            the connected client
06943 
06944   Input:
06945     char  *buffer_name      Name of buffer
06946     int   socket            Network socket to client
06947 
06948   Output:
06949     none
06950 
06951   Function value:
06952     BM_SUCCESS              Successful completion
06953 
06954 \********************************************************************/
06955 {
06956    char buffer[32];
06957    NET_COMMAND *nc;
06958    INT i, convert_flags;
06959    static DWORD last_time = 0;
06960 
06961    for (i = 0; i < _buffer_entries; i++)
06962       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
06963          break;
06964    if (i == _buffer_entries)
06965       return BM_INVALID_HANDLE;
06966 
06967    /* don't send notification if client has no callback defined
06968       to receive events -> client calls bm_receive_event manually */
06969    if (!_buffer[i].callback)
06970       return DB_SUCCESS;
06971 
06972    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06973 
06974    /* only send notification once each 500ms */
06975    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
06976       return DB_SUCCESS;
06977 
06978    last_time = ss_millitime();
06979 
06980    if (convert_flags & CF_ASCII) {
06981       sprintf(buffer, "MSG_BM&%s", buffer_name);
06982       send_tcp(socket, buffer, strlen(buffer) + 1, 0);
06983    } else {
06984       nc = (NET_COMMAND *) buffer;
06985 
06986       nc->header.routine_id = MSG_BM;
06987       nc->header.param_size = 0;
06988 
06989       if (convert_flags) {
06990          rpc_convert_single(&nc->header.routine_id, TID_DWORD,
06991                             RPC_OUTGOING, convert_flags);
06992          rpc_convert_single(&nc->header.param_size, TID_DWORD,
06993                             RPC_OUTGOING, convert_flags);
06994       }
06995 
06996       /* send the update notification to the client */
06997       send_tcp(socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
06998    }
06999 
07000    return BM_SUCCESS;
07001 }
07002 
07003 /********************************************************************/
07004 INT bm_poll_event(INT flag)
07005 /********************************************************************\
07006 
07007   Routine: bm_poll_event
07008 
07009   Purpose: Poll an event from a remote server. Gets called by
07010            rpc_client_dispatch
07011 
07012   Input:
07013     INT flag         TRUE if called from cm_yield
07014 
07015   Output:
07016     none
07017 
07018   Function value:
07019     TRUE             More events are waiting
07020     FALSE            No more events are waiting
07021     SS_ABORT         Network connection broken
07022 
07023 \********************************************************************/
07024 {
07025    INT status, size, i, request_id;
07026    DWORD start_time;
07027    BOOL bMore;
07028    static BOOL bMoreLast = FALSE;
07029 
07030    if (_event_buffer_size == 0) {
07031       _event_buffer = (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
07032       if (!_event_buffer) {
07033          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer");
07034          return SS_ABORT;
07035       }
07036       _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
07037    }
07038 
07039    start_time = ss_millitime();
07040 
07041    /* if we got event notification, turn off read_wait */
07042    if (!flag)
07043       bm_mark_read_waiting(FALSE);
07044 
07045    /* if called from yield, return if no more events */
07046    if (flag) {
07047       if (!bMoreLast)
07048          return FALSE;
07049    }
07050 
07051    bMore = FALSE;
07052 
07053    /* loop over all requests */
07054    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07055       /* continue if no dispatcher set (manual bm_receive_event) */
07056       if (_request_list[request_id].dispatcher == NULL)
07057          continue;
07058 
07059       do {
07060          /* receive event */
07061          size = _event_buffer_size;
07062          status = bm_receive_event(_request_list[request_id].buffer_handle,
07063                                    _event_buffer, &size, ASYNC);
07064 
07065          /* call user function if successful */
07066          if (status == BM_SUCCESS)
07067             /* search proper request for this event */
07068             for (i = 0; i < _request_list_entries; i++)
07069                if ((_request_list[i].buffer_handle ==
07070                     _request_list[request_id].buffer_handle) &&
07071                    bm_match_event(_request_list[i].event_id,
07072                                   _request_list[i].trigger_mask, _event_buffer)) {
07073                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07074                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07075                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07076                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07077                                          _request_list[i].dispatcher);
07078                   else
07079                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07080                                                  _event_buffer,
07081                                                  (void
07082                                                   *) (((EVENT_HEADER *) _event_buffer) +
07083                                                       1));
07084                }
07085 
07086          /* break if no more events */
07087          if (status == BM_ASYNC_RETURN)
07088             break;
07089 
07090          /* break if server died */
07091          if (status == RPC_NET_ERROR)
07092             return SS_ABORT;
07093 
07094          /* stop after one second */
07095          if (ss_millitime() - start_time > 1000) {
07096             bMore = TRUE;
07097             break;
07098          }
07099 
07100       } while (TRUE);
07101    }
07102 
07103    if (!bMore)
07104       bm_mark_read_waiting(TRUE);
07105 
07106    bMoreLast = bMore;
07107 
07108    return bMore;
07109 }
07110 
07111 /**dox***************************************************************/
07112 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07113 
07114 /********************************************************************/
07115 /** 
07116 Clears event buffer and cache.
07117 If an event buffer is large and a consumer is slow in analyzing
07118 events, events are usually received some time after they are produced.
07119 This effect is even more experienced if a read cache is used
07120 (via bm_set_cache_size()).
07121 When changes to the hardware are made in the experience, the consumer will then
07122 still analyze old events before any new event which reflects the hardware change.
07123 Users can be fooled by looking at histograms which reflect the hardware change
07124 many seconds after they have been made.
07125 
07126 To overcome this potential problem, the analyzer can call
07127 bm_empty_buffers() just after the hardware change has been made which
07128 skips all old events contained in event buffers and read caches.
07129 Technically this is done by forwarding the read pointer of the client.
07130 No events are really deleted, they are still visible to other clients like
07131 the logger.
07132 
07133 Note that the front-end also contains write buffers which can delay the
07134 delivery of events.
07135 The standard front-end framework mfe.c reduces this effect by flushing
07136 all buffers once every second.
07137 @return BM_SUCCESS
07138 */
07139 INT bm_empty_buffers()
07140 {
07141    if (rpc_is_remote())
07142       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07143 
07144 #ifdef LOCAL_ROUTINES
07145    {
07146       INT index, server_type, server_conn, tid;
07147       BUFFER *pbuf;
07148       BUFFER_CLIENT *pclient;
07149 
07150       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07151       server_conn = rpc_get_server_acception();
07152       tid = ss_gettid();
07153 
07154       /* go through all buffers */
07155       for (index = 0; index < _buffer_entries; index++) {
07156          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07157             continue;
07158 
07159          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07160             continue;
07161 
07162          if (!_buffer[index].attached)
07163             continue;
07164 
07165          pbuf = &_buffer[index];
07166 
07167          /* empty cache */
07168          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07169 
07170          /* set read pointer to write pointer */
07171          pclient = (pbuf->buffer_header)->client + pbuf->client_index;
07172          bm_lock_buffer(index + 1);
07173          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07174          bm_unlock_buffer(index + 1);
07175       }
07176 
07177    }
07178 #endif                          /* LOCAL_ROUTINES */
07179 
07180    return BM_SUCCESS;
07181 }
07182 
07183 /**dox***************************************************************/
07184 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07185 
07186 #define MAX_DEFRAG_EVENTS 10
07187 
07188 typedef struct {
07189    WORD event_id;
07190    DWORD data_size;
07191    DWORD received;
07192    EVENT_HEADER *pevent;
07193 } EVENT_DEFRAG_BUFFER;
07194 
07195 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07196 
07197 /********************************************************************/
07198 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07199                          EVENT_HEADER * pevent, void *pdata,
07200                          void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *, void *))
07201 /********************************************************************\
07202 
07203   Routine: bm_defragment_event
07204 
07205   Purpose: Called internally from the event receiving routines
07206            bm_push_event and bm_poll_event to recombine event
07207            fragments and call the user callback routine upon
07208            completion.
07209 
07210   Input:
07211     HNDLE buffer_handle  Handle for the buffer containing event
07212     HNDLE request_id     Handle for event request
07213     EVENT_HEADER *pevent Pointer to event header
07214     void *pata           Pointer to event data
07215     dispatcher()         User callback routine
07216 
07217   Output:
07218     <calls dispatcher() after successfull recombination of event>
07219 
07220   Function value:
07221     void
07222 
07223 \********************************************************************/
07224 {
07225    INT i;
07226    static int j = -1;
07227 
07228    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07229       /*---- start new event ----*/
07230 
07231       printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number,
07232              pevent->event_id);
07233       /* check if fragments already stored */
07234       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07235          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07236             break;
07237 
07238       if (i < MAX_DEFRAG_EVENTS) {
07239          free(defrag_buffer[i].pevent);
07240          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07241          cm_msg(MERROR, "bm_defragement_event",
07242                 "Received new event with ID %d while old fragments were not completed",
07243                 (pevent->event_id & 0x0FFF));
07244       }
07245 
07246       /* search new slot */
07247       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07248          if (defrag_buffer[i].event_id == 0)
07249             break;
07250 
07251       if (i == MAX_DEFRAG_EVENTS) {
07252          cm_msg(MERROR, "bm_defragment_event",
07253                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07254          return;
07255       }
07256 
07257       /* check event size */
07258       if (pevent->data_size != sizeof(DWORD)) {
07259          cm_msg(MERROR, "bm_defragment_event",
07260                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07261                 pevent->data_size, sizeof(DWORD));
07262          return;
07263       }
07264 
07265       /* setup defragment buffer */
07266       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07267       defrag_buffer[i].data_size = *(DWORD *) pdata;
07268       defrag_buffer[i].received = 0;
07269       defrag_buffer[i].pevent =
07270           (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07271 
07272       if (defrag_buffer[i].pevent == NULL) {
07273          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07274          cm_msg(MERROR, "bm_defragement_event",
07275                 "Not enough memory to allocate event defragment buffer");
07276          return;
07277       }
07278 
07279       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07280       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07281       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07282 
07283       printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
07284              pevent->serial_number, defrag_buffer[i].data_size);
07285       j = 0;
07286 
07287       return;
07288    }
07289 
07290    /* search buffer for that event */
07291    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07292       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07293          break;
07294 
07295    if (i == MAX_DEFRAG_EVENTS) {
07296       /* no buffer available -> no first fragment received */
07297       free(defrag_buffer[i].pevent);
07298       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07299       cm_msg(MERROR, "bm_defragement_event",
07300              "Received fragment without first fragment (ID %d) Ser#:%d",
07301              pevent->event_id & 0x0FFF, pevent->serial_number);
07302       printf("Received fragment without first fragment (ID 0x%x) Ser#:%d Sz:%d\n",
07303              pevent->event_id, pevent->serial_number, pevent->data_size);
07304       return;
07305    }
07306 
07307    /* add fragment to buffer */
07308    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
07309       free(defrag_buffer[i].pevent);
07310       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07311       cm_msg(MERROR, "bm_defragement_event",
07312              "Received fragments with more data (%d) than event size (%d)",
07313              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
07314       return;
07315    }
07316 
07317    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07318           defrag_buffer[i].received, pdata, pevent->data_size);
07319 
07320    defrag_buffer[i].received += pevent->data_size;
07321 
07322    printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
07323           defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
07324 
07325 
07326    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
07327       /* event complete */
07328       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent,
07329                  defrag_buffer[i].pevent + 1);
07330       free(defrag_buffer[i].pevent);
07331       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07332    }
07333 }
07334 
07335 /**dox***************************************************************/
07336 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07337 
07338 /**dox***************************************************************/
07339                             /** @} *//* end of bmfunctionc */
07340 
07341 /**dox***************************************************************/
07342 /** @addtogroup rpcfunctionc
07343  *  
07344  *  @{  */
07345 
07346 /**dox***************************************************************/
07347 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07348 
07349 /********************************************************************\
07350 *                                                                    *
07351 *                         RPC functions                              *
07352 *                                                                    *
07353 \********************************************************************/
07354 
07355 /* globals */
07356 
07357 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
07358 RPC_SERVER_CONNECTION _server_connection;
07359 
07360 static int _lsock;
07361 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
07362 static INT _server_acception_index = 0;
07363 static INT _server_type;
07364 static char _server_name[256];
07365 
07366 static RPC_LIST *rpc_list = NULL;
07367 
07368 int _opt_tcp_size = OPT_TCP_SIZE;
07369 
07370 
07371 /********************************************************************\
07372 *                       conversion functions                         *
07373 \********************************************************************/
07374 
07375 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
07376 {
07377    *convert_flags = 0;
07378 
07379    /* big/little endian conversion */
07380    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
07381         (hw_type & DRI_LITTLE_ENDIAN)) ||
07382        ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
07383       *convert_flags |= CF_ENDIAN;
07384 
07385    /* float conversion between IEEE and VAX G */
07386    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
07387       *convert_flags |= CF_VAX2IEEE;
07388 
07389    /* float conversion between VAX G and IEEE */
07390    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
07391       *convert_flags |= CF_IEEE2VAX;
07392 
07393    /* ASCII format */
07394    if (remote_hw_type & DR_ASCII)
07395       *convert_flags |= CF_ASCII;
07396 }
07397 
07398 /********************************************************************/
07399 void rpc_get_convert_flags(INT * convert_flags)
07400 {
07401    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE),
07402                           _server_connection.remote_hw_type, convert_flags);
07403 }
07404 
07405 /********************************************************************/
07406 void rpc_ieee2vax_float(float *var)
07407 {
07408    unsigned short int lo, hi;
07409 
07410    /* swap hi and lo word */
07411    lo = *((short int *) (var) + 1);
07412    hi = *((short int *) (var));
07413 
07414    /* correct exponent */
07415    if (lo != 0)
07416       lo += 0x100;
07417 
07418    *((short int *) (var) + 1) = hi;
07419    *((short int *) (var)) = lo;
07420 }
07421 
07422 void rpc_vax2ieee_float(float *var)
07423 {
07424    unsigned short int lo, hi;
07425 
07426    /* swap hi and lo word */
07427    lo = *((short int *) (var) + 1);
07428    hi = *((short int *) (var));
07429 
07430    /* correct exponent */
07431    if (hi != 0)
07432       hi -= 0x100;
07433 
07434    *((short int *) (var) + 1) = hi;
07435    *((short int *) (var)) = lo;
07436 
07437 }
07438 
07439 void rpc_vax2ieee_double(double *var)
07440 {
07441    unsigned short int i1, i2, i3, i4;
07442 
07443    /* swap words */
07444    i1 = *((short int *) (var) + 3);
07445    i2 = *((short int *) (var) + 2);
07446    i3 = *((short int *) (var) + 1);
07447    i4 = *((short int *) (var));
07448 
07449    /* correct exponent */
07450    if (i4 != 0)
07451       i4 -= 0x20;
07452 
07453    *((short int *) (var) + 3) = i4;
07454    *((short int *) (var) + 2) = i3;
07455    *((short int *) (var) + 1) = i2;
07456    *((short int *) (var)) = i1;
07457 }
07458 
07459 void rpc_ieee2vax_double(double *var)
07460 {
07461    unsigned short int i1, i2, i3, i4;
07462 
07463    /* swap words */
07464    i1 = *((short int *) (var) + 3);
07465    i2 = *((short int *) (var) + 2);
07466    i3 = *((short int *) (var) + 1);
07467    i4 = *((short int *) (var));
07468 
07469    /* correct exponent */
07470    if (i1 != 0)
07471       i1 += 0x20;
07472 
07473    *((short int *) (var) + 3) = i4;
07474    *((short int *) (var) + 2) = i3;
07475    *((short int *) (var) + 1) = i2;
07476    *((short int *) (var)) = i1;
07477 }
07478 
07479 /********************************************************************/
07480 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
07481 {
07482 
07483    if (convert_flags & CF_ENDIAN) {
07484       if (tid == TID_WORD || tid == TID_SHORT)
07485          WORD_SWAP(data);
07486       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
07487          DWORD_SWAP(data);
07488       if (tid == TID_DOUBLE)
07489          QWORD_SWAP(data);
07490    }
07491 
07492    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
07493        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
07494       if (tid == TID_FLOAT)
07495          rpc_ieee2vax_float((float *) data);
07496       if (tid == TID_DOUBLE)
07497          rpc_ieee2vax_double((double *) data);
07498    }
07499 
07500    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
07501        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
07502       if (tid == TID_FLOAT)
07503          rpc_vax2ieee_float((float *) data);
07504       if (tid == TID_DOUBLE)
07505          rpc_vax2ieee_double((double *) data);
07506    }
07507 }
07508 
07509 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
07510 /********************************************************************\
07511 
07512   Routine: rpc_convert_data
07513 
07514   Purpose: Convert data format between differenct computers
07515 
07516   Input:
07517     void   *data            Pointer to data
07518     INT    tid              Type ID of data, one of TID_xxx
07519     INT    flags            Combination of following flags:
07520                               RPC_IN: data is input parameter
07521                               RPC_OUT: data is output variable
07522                               RPC_FIXARRAY, RPC_VARARRAY: data is array
07523                                 of "size" bytes (see next param.)
07524                               RPC_OUTGOING: data is outgoing
07525     INT    total_size       Size of bytes of data. Used for variable
07526                             length arrays.
07527     INT    convert_flags    Flags for data conversion
07528 
07529   Output:
07530     void   *data            Is converted according to _convert_flag
07531                             value
07532 
07533   Function value:
07534     RPC_SUCCESS             Successful completion
07535 
07536 \********************************************************************/
07537 {
07538    INT i, n, single_size;
07539    char *p;
07540 
07541    /* convert array */
07542    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
07543       single_size = tid_size[tid];
07544       /* don't convert TID_ARRAY & TID_STRUCT */
07545       if (single_size == 0)
07546          return;
07547 
07548       n = total_size / single_size;
07549 
07550       for (i = 0; i < n; i++) {
07551          p = (char *) data + (i * single_size);
07552          rpc_convert_single(p, tid, flags, convert_flags);
07553       }
07554    } else {
07555       rpc_convert_single(data, tid, flags, convert_flags);
07556    }
07557 }
07558 
07559 /********************************************************************\
07560 *                       type ID functions                            *
07561 \********************************************************************/
07562 
07563 INT rpc_tid_size(INT id)
07564 {
07565    if (id < TID_LAST)
07566       return tid_size[id];
07567 
07568    return 0;
07569 }
07570 
07571 char *rpc_tid_name(INT id)
07572 {
07573    if (id < TID_LAST)
07574       return tid_name[id];
07575    else
07576       return "<unknown>";
07577 }
07578 
07579 
07580 /**dox***************************************************************/
07581 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07582 
07583 /********************************************************************\
07584 *                        client functions                            *
07585 \********************************************************************/
07586 
07587 /********************************************************************/
07588 /**
07589 Register RPC client for standalone mode (without standard
07590            midas server)
07591 @param list           Array of RPC_LIST structures containing
07592                             function IDs and parameter definitions.
07593                             The end of the list must be indicated by
07594                             a function ID of zero.
07595 @param name          Name of this client
07596 @return RPC_SUCCESS
07597 */
07598 INT rpc_register_client(char *name, RPC_LIST * list)
07599 {
07600    rpc_set_name(name);
07601    rpc_register_functions(rpc_get_internal_list(0), NULL);
07602    rpc_register_functions(list, NULL);
07603 
07604    return RPC_SUCCESS;
07605 }
07606 
07607 /********************************************************************/
07608 /**
07609 Register a set of RPC functions (both as clients or servers)
07610 @param new_list       Array of RPC_LIST structures containing
07611                             function IDs and parameter definitions.
07612                             The end of the list must be indicated by
07613                             a function ID of zero.
07614 @param func          Default dispatch function
07615 
07616 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
07617 */
07618 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
07619 {
07620    INT i, j, iold, inew;
07621 
07622    /* count number of new functions */
07623    for (i = 0; new_list[i].id != 0; i++) {
07624       /* check double defined functions */
07625       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
07626          if (rpc_list[j].id == new_list[i].id)
07627             return RPC_DOUBLE_DEFINED;
07628    }
07629    inew = i;
07630 
07631    /* count number of existing functions */
07632    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
07633    iold = i;
07634 
07635    /* allocate new memory for rpc_list */
07636    if (rpc_list == NULL)
07637       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
07638    else
07639       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
07640 
07641    if (rpc_list == NULL) {
07642       cm_msg(MERROR, "rpc_register_functions", "out of memory");
07643       return RPC_NO_MEMORY;
07644    }
07645 
07646    /* append new functions */
07647    for (i = iold; i < iold + inew; i++) {
07648       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
07649 
07650       /* set default dispatcher */
07651       if (rpc_list[i].dispatch == NULL)
07652          rpc_list[i].dispatch = func;
07653 
07654       /* check valid ID for user functions */
07655       if (new_list != rpc_get_internal_list(0) &&
07656           new_list != rpc_get_internal_list(1) &&
07657           (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
07658          cm_msg(MERROR, "rpc_register_functions",
07659                 "registered RPC function with invalid ID");
07660    }
07661 
07662    /* mark end of list */
07663    rpc_list[i].id = 0;
07664 
07665    return RPC_SUCCESS;
07666 }
07667 
07668 
07669 
07670 /**dox***************************************************************/
07671 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07672 
07673 /********************************************************************/
07674 INT rpc_deregister_functions()
07675 /********************************************************************\
07676 
07677   Routine: rpc_deregister_functions
07678 
07679   Purpose: Free memory of previously registered functions
07680 
07681   Input:
07682     none
07683 
07684   Output:
07685     none
07686 
07687   Function value:
07688     RPC_SUCCESS              Successful completion
07689 
07690 \********************************************************************/
07691 {
07692    if (rpc_list)
07693       M_FREE(rpc_list);
07694    rpc_list = NULL;
07695 
07696    return RPC_SUCCESS;
07697 }
07698 
07699 
07700 /********************************************************************/
07701 INT rpc_register_function(INT id, INT(*func) (INT, void **))
07702 /********************************************************************\
07703 
07704   Routine: rpc_register_function
07705 
07706   Purpose: Replace a dispatch function for a specific rpc routine
07707 
07708   Input:
07709     INT      id             RPC ID
07710     INT      *func          New dispatch function
07711 
07712   Output:
07713    <implicit: func gets copied to rpc_list>
07714 
07715   Function value:
07716    RPC_SUCCESS              Successful completion
07717    RPC_INVALID_ID           RPC ID not found
07718 
07719 \********************************************************************/
07720 {
07721    INT i;
07722 
07723    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
07724       if (rpc_list[i].id == id)
07725          break;
07726 
07727    if (rpc_list[i].id == id)
07728       rpc_list[i].dispatch = func;
07729    else
07730       return RPC_INVALID_ID;
07731 
07732    return RPC_SUCCESS;
07733 }
07734 
07735 
07736 /********************************************************************/
07737 INT rpc_client_dispatch(int sock)
07738 /********************************************************************\
07739 
07740   Routine: rpc_client_dispatch
07741 
07742   Purpose: Gets called whenever a client receives data from the
07743            server. Get set via rpc_connect. Internal use only.
07744 
07745 \********************************************************************/
07746 {
07747    INT hDB, hKey, n;
07748    NET_COMMAND *nc;
07749    INT status = 0;
07750    char net_buffer[256];
07751 
07752    nc = (NET_COMMAND *) net_buffer;
07753 
07754    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07755    if (n <= 0)
07756       return SS_ABORT;
07757 
07758    if (nc->header.routine_id == MSG_ODB) {
07759       /* update a changed record */
07760       hDB = *((INT *) nc->param);
07761       hKey = *((INT *) nc->param + 1);
07762       status = db_update_record(hDB, hKey, 0);
07763    }
07764 
07765    else if (nc->header.routine_id == MSG_WATCHDOG) {
07766       nc->header.routine_id = 1;
07767       nc->header.param_size = 0;
07768       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07769       status = RPC_SUCCESS;
07770    }
07771 
07772    else if (nc->header.routine_id == MSG_BM) {
07773       fd_set readfds;
07774       struct timeval timeout;
07775 
07776       /* receive further messages to empty TCP queue */
07777       do {
07778          FD_ZERO(&readfds);
07779          FD_SET(sock, &readfds);
07780 
07781          timeout.tv_sec = 0;
07782          timeout.tv_usec = 0;
07783 
07784          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
07785 
07786          if (FD_ISSET(sock, &readfds)) {
07787             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07788             if (n <= 0)
07789                return SS_ABORT;
07790 
07791             if (nc->header.routine_id == MSG_ODB) {
07792                /* update a changed record */
07793                hDB = *((INT *) nc->param);
07794                hKey = *((INT *) nc->param + 1);
07795                status = db_update_record(hDB, hKey, 0);
07796             }
07797 
07798             else if (nc->header.routine_id == MSG_WATCHDOG) {
07799                nc->header.routine_id = 1;
07800                nc->header.param_size = 0;
07801                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07802                status = RPC_SUCCESS;
07803             }
07804          }
07805 
07806       } while (FD_ISSET(sock, &readfds));
07807 
07808       /* poll event from server */
07809       status = bm_poll_event(FALSE);
07810    }
07811 
07812    return status;
07813 }
07814 
07815 
07816 /********************************************************************/
07817 INT rpc_client_connect(char *host_name, INT port, char *client_name, HNDLE * hConnection)
07818 /********************************************************************\
07819 
07820   Routine: rpc_client_connect
07821 
07822   Purpose: Establish a network connection to a remote client
07823 
07824   Input:
07825     char *host_name          IP address of host to connect to.
07826     INT  port                TPC port to connect to.
07827     char *clinet_name        Client program name
07828 
07829   Output:
07830     HNDLE *hConnection       Handle for new connection which can be used
07831                              in future rpc_call(hConnection....) calls
07832 
07833   Function value:
07834     RPC_SUCCESS              Successful completion
07835     RPC_NET_ERROR            Error in socket call
07836     RPC_NO_CONNECTION        Maximum number of connections reached
07837     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
07838 
07839 \********************************************************************/
07840 {
07841    INT i, status, index;
07842    struct sockaddr_in bind_addr;
07843    INT sock;
07844    INT remote_hw_type, hw_type;
07845    char str[200];
07846    char version[32], v1[32];
07847    char local_prog_name[NAME_LENGTH];
07848    char local_host_name[HOST_NAME_LENGTH];
07849    struct hostent *phe;
07850 
07851 #ifdef OS_WINNT
07852    {
07853       WSADATA WSAData;
07854 
07855       /* Start windows sockets */
07856       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
07857          return RPC_NET_ERROR;
07858    }
07859 #endif
07860 
07861    /* check if cm_connect_experiment was called */
07862    if (_client_name[0] == 0) {
07863       cm_msg(MERROR, "rpc_client_connect",
07864              "cm_connect_experiment/rpc_set_name not called");
07865       return RPC_NOT_REGISTERED;
07866    }
07867 
07868    /* check for broken connections */
07869    rpc_client_check();
07870 
07871    /* check if connection already exists */
07872    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07873       if (_client_connection[i].send_sock != 0 &&
07874           strcmp(_client_connection[i].host_name, host_name) == 0 &&
07875           _client_connection[i].port == port) {
07876          *hConnection = i + 1;
07877          return RPC_SUCCESS;
07878       }
07879 
07880    /* search for free entry */
07881    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07882       if (_client_connection[i].send_sock == 0)
07883          break;
07884 
07885    /* open new network connection */
07886    if (i == MAX_RPC_CONNECTION) {
07887       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
07888       return RPC_NO_CONNECTION;
07889    }
07890 
07891    /* create a new socket for connecting to remote server */
07892    sock = socket(AF_INET, SOCK_STREAM, 0);
07893    if (sock == -1) {
07894       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
07895       return RPC_NET_ERROR;
07896    }
07897 
07898    index = i;
07899    strcpy(_client_connection[index].host_name, host_name);
07900    strcpy(_client_connection[index].client_name, client_name);
07901    _client_connection[index].port = port;
07902    _client_connection[index].exp_name[0] = 0;
07903    _client_connection[index].transport = RPC_TCP;
07904    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
07905    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
07906 
07907    /* connect to remote node */
07908    memset(&bind_addr, 0, sizeof(bind_addr));
07909    bind_addr.sin_family = AF_INET;
07910    bind_addr.sin_addr.s_addr = 0;
07911    bind_addr.sin_port = htons((short) port);
07912 
07913 #ifdef OS_VXWORKS
07914    {
07915       INT host_addr;
07916 
07917       host_addr = hostGetByName(host_name);
07918       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
07919    }
07920 #else
07921    phe = gethostbyname(host_name);
07922    if (phe == NULL) {
07923       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
07924       return RPC_NET_ERROR;
07925    }
07926    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
07927 #endif
07928 
07929 #ifdef OS_UNIX
07930    do {
07931       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
07932 
07933       /* don't return if an alarm signal was cought */
07934    } while (status == -1 && errno == EINTR);
07935 #else
07936    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
07937 #endif
07938 
07939    if (status != 0) {
07940       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
07941          message should be displayed by application */
07942       return RPC_NET_ERROR;
07943    }
07944 
07945    /* set TCP_NODELAY option for better performance */
07946 #ifdef OS_VXWORKS
07947    i = 1;
07948    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
07949 #endif
07950 
07951    /* send local computer info */
07952    rpc_get_name(local_prog_name);
07953    gethostname(local_host_name, sizeof(local_host_name));
07954 
07955    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
07956    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name,
07957            local_host_name);
07958 
07959    send(sock, str, strlen(str) + 1, 0);
07960 
07961    /* receive remote computer info */
07962    i = recv_string(sock, str, sizeof(str), 10000);
07963    if (i <= 0) {
07964       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s",
07965              str);
07966       return RPC_NET_ERROR;
07967    }
07968 
07969    remote_hw_type = version[0] = 0;
07970    sscanf(str, "%d %s", &remote_hw_type, version);
07971    _client_connection[index].remote_hw_type = remote_hw_type;
07972    _client_connection[index].send_sock = sock;
07973 
07974    /* print warning if version patch level doesn't agree */
07975    strcpy(v1, version);
07976    if (strchr(v1, '.'))
07977       if (strchr(strchr(v1, '.') + 1, '.'))
07978          *strchr(strchr(v1, '.') + 1, '.') = 0;
07979 
07980    strcpy(str, cm_get_version());
07981    if (strchr(str, '.'))
07982       if (strchr(strchr(str, '.') + 1, '.'))
07983          *strchr(strchr(str, '.') + 1, '.') = 0;
07984 
07985    if (strcmp(v1, str) != 0) {
07986       sprintf(str, "remote MIDAS version %s differs from local version %s",
07987               version, cm_get_version());
07988       cm_msg(MERROR, "rpc_client_connect", str);
07989    }
07990 
07991    *hConnection = index + 1;
07992 
07993    return RPC_SUCCESS;
07994 }
07995 
07996 
07997 /********************************************************************/
07998 void rpc_client_check()
07999 /********************************************************************\
08000 
08001   Routine: rpc_client_check
08002 
08003   Purpose: Check all client connections if remote client closed link
08004 
08005   Function value:
08006     RPC_SUCCESS              Successful completion
08007     RPC_NET_ERROR            Error in socket call
08008     RPC_NO_CONNECTION        Maximum number of connections reached
08009     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08010 
08011 \********************************************************************/
08012 {
08013    INT i, status;
08014 
08015    /* check for broken connections */
08016    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08017       if (_client_connection[i].send_sock != 0) {
08018          int sock;
08019          fd_set readfds;
08020          struct timeval timeout;
08021          char buffer[64];
08022 
08023          sock = _client_connection[i].send_sock;
08024          FD_ZERO(&readfds);
08025          FD_SET(sock, &readfds);
08026 
08027          timeout.tv_sec = 0;
08028          timeout.tv_usec = 0;
08029 
08030          do {
08031             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08032          } while (status == -1);        /* dont return if an alarm signal was cought */
08033 
08034          if (FD_ISSET(sock, &readfds)) {
08035             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
08036 
08037             if (equal_ustring(buffer, "EXIT")) {
08038                /* normal exit */
08039                closesocket(sock);
08040                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08041             }
08042 
08043             if (status <= 0) {
08044                cm_msg(MERROR, "rpc_client_check",
08045                       "Connection broken to \"%s\" on host %s",
08046                       _client_connection[i].client_name, _client_connection[i].host_name);
08047 
08048                /* connection broken -> reset */
08049                closesocket(sock);
08050                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08051             }
08052          }
08053       }
08054 }
08055 
08056 
08057 /********************************************************************/
08058 INT rpc_server_connect(char *host_name, char *exp_name)
08059 /********************************************************************\
08060 
08061   Routine: rpc_server_connect
08062 
08063   Purpose: Extablish a network connection to a remote MIDAS
08064            server using a callback scheme.
08065 
08066   Input:
08067     char *host_name         IP address of host to connect to.
08068 
08069     INT  port               TPC port to connect to.
08070 
08071     char *exp_name          Name of experiment to connect to. By using
08072                             this name, several experiments (e.g. online
08073                             DAQ and offline analysis) can run simultan-
08074                             eously on the same host.
08075 
08076   Output:
08077     none
08078 
08079   Function value:
08080     RPC_SUCCESS              Successful completion
08081     RPC_NET_ERROR            Error in socket call
08082     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08083     CM_UNDEF_EXP             Undefined experiment on server
08084 
08085 \********************************************************************/
08086 {
08087    INT i, status, flag;
08088    struct sockaddr_in bind_addr;
08089    INT sock, lsock1, lsock2, lsock3;
08090    INT listen_port1, listen_port2, listen_port3;
08091    INT remote_hw_type, hw_type;
08092    unsigned int size;
08093    char str[200], version[32], v1[32];
08094    char local_prog_name[NAME_LENGTH];
08095    struct hostent *phe;
08096    fd_set readfds;
08097    struct timeval timeout;
08098 
08099 #ifdef OS_WINNT
08100    {
08101       WSADATA WSAData;
08102 
08103       /* Start windows sockets */
08104       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08105          return RPC_NET_ERROR;
08106    }
08107 #endif
08108 
08109    /* check if local connection */
08110    if (host_name[0] == 0)
08111       return RPC_SUCCESS;
08112 
08113    /* register system functions */
08114    rpc_register_functions(rpc_get_internal_list(0), NULL);
08115 
08116    /* check if cm_connect_experiment was called */
08117    if (_client_name[0] == 0) {
08118       cm_msg(MERROR, "rpc_server_connect",
08119              "cm_connect_experiment/rpc_set_name not called");
08120       return RPC_NOT_REGISTERED;
08121    }
08122 
08123    /* check if connection already exists */
08124    if (_server_connection.send_sock != 0)
08125       return RPC_SUCCESS;
08126 
08127    strcpy(_server_connection.host_name, host_name);
08128    strcpy(_server_connection.exp_name, exp_name);
08129    _server_connection.transport = RPC_TCP;
08130    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08131 
08132    /* create new TCP sockets for listening */
08133    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08134    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08135    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08136    if (lsock3 == -1) {
08137       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08138       return RPC_NET_ERROR;
08139    }
08140 
08141    flag = 1;
08142    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08143    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08144    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08145 
08146    /* let OS choose any port number */
08147    memset(&bind_addr, 0, sizeof(bind_addr));
08148    bind_addr.sin_family = AF_INET;
08149    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08150    bind_addr.sin_port = 0;
08151 
08152    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08153    bind_addr.sin_port = 0;
08154    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08155    bind_addr.sin_port = 0;
08156    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08157    if (status < 0) {
08158       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08159       return RPC_NET_ERROR;
08160    }
08161 
08162    /* listen for connection */
08163    status = listen(lsock1, 1);
08164    status = listen(lsock2, 1);
08165    status = listen(lsock3, 1);
08166    if (status < 0) {
08167       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08168       return RPC_NET_ERROR;
08169    }
08170 
08171    /* find out which port OS has chosen */
08172    size = sizeof(bind_addr);
08173    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *)&size);
08174    listen_port1 = ntohs(bind_addr.sin_port);
08175    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *)&size);
08176    listen_port2 = ntohs(bind_addr.sin_port);
08177    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *)&size);
08178    listen_port3 = ntohs(bind_addr.sin_port);
08179 
08180    /* create a new socket for connecting to remote server */
08181    sock = socket(AF_INET, SOCK_STREAM, 0);
08182    if (sock == -1) {
08183       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08184       return RPC_NET_ERROR;
08185    }
08186 
08187    /* connect to remote node */
08188    memset(&bind_addr, 0, sizeof(bind_addr));
08189    bind_addr.sin_family = AF_INET;
08190    bind_addr.sin_addr.s_addr = 0;
08191    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08192 
08193 #ifdef OS_VXWORKS
08194    {
08195       INT host_addr;
08196 
08197       host_addr = hostGetByName(host_name);
08198       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08199    }
08200 #else
08201    phe = gethostbyname(host_name);
08202    if (phe == NULL) {
08203       cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08204       return RPC_NET_ERROR;
08205    }
08206    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08207 #endif
08208 
08209 #ifdef OS_UNIX
08210    do {
08211       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08212 
08213       /* don't return if an alarm signal was cought */
08214    } while (status == -1 && errno == EINTR);
08215 #else
08216    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08217 #endif
08218 
08219    if (status != 0) {
08220 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08221       return RPC_NET_ERROR;
08222    }
08223 
08224    /* connect to experiment */
08225    if (exp_name[0] == 0)
08226       sprintf(str, "C %d %d %d %s Default",
08227               listen_port1, listen_port2, listen_port3, cm_get_version());
08228    else
08229       sprintf(str, "C %d %d %d %s %s",
08230               listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08231 
08232    send(sock, str, strlen(str) + 1, 0);
08233    i = recv_string(sock, str, sizeof(str), 10000);
08234    closesocket(sock);
08235    if (i <= 0) {
08236       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08237       return RPC_NET_ERROR;
08238    }
08239 
08240    status = version[0] = 0;
08241    sscanf(str, "%d %s", &status, version);
08242 
08243    if (status == 2) {
08244 /*  message "undefined experiment" should be displayed by application */
08245       return CM_UNDEF_EXP;
08246    }
08247 
08248    /* print warning if version patch level doesn't agree */
08249    strcpy(v1, version);
08250    if (strchr(v1, '.'))
08251       if (strchr(strchr(v1, '.') + 1, '.'))
08252          *strchr(strchr(v1, '.') + 1, '.') = 0;
08253 
08254    strcpy(str, cm_get_version());
08255    if (strchr(str, '.'))
08256       if (strchr(strchr(str, '.') + 1, '.'))
08257          *strchr(strchr(str, '.') + 1, '.') = 0;
08258 
08259    if (strcmp(v1, str) != 0) {
08260       sprintf(str, "remote MIDAS version %s differs from local version %s",
08261               version, cm_get_version());
08262       cm_msg(MERROR, "rpc_server_connect", str);
08263    }
08264 
08265    /* wait for callback on send and recv socket with timeout */
08266    FD_ZERO(&readfds);
08267    FD_SET(lsock1, &readfds);
08268    FD_SET(lsock2, &readfds);
08269    FD_SET(lsock3, &readfds);
08270 
08271    timeout.tv_sec = 5;
08272    timeout.tv_usec = 0;
08273 
08274    do {
08275       status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08276 
08277       /* if an alarm signal was cought, restart select with reduced timeout */
08278       if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
08279          timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
08280 
08281    } while (status == -1);      /* dont return if an alarm signal was cought */
08282 
08283    if (!FD_ISSET(lsock1, &readfds)) {
08284       cm_msg(MERROR, "rpc_server_connect",
08285              "mserver subprocess could not be started (check path)");
08286       closesocket(lsock1);
08287       closesocket(lsock2);
08288       closesocket(lsock3);
08289       return RPC_NET_ERROR;
08290    }
08291 
08292    size = sizeof(bind_addr);
08293 
08294    _server_connection.send_sock = accept(lsock1, (struct sockaddr *) &bind_addr, (int *)&size);
08295 
08296    _server_connection.recv_sock = accept(lsock2, (struct sockaddr *) &bind_addr, (int *)&size);
08297 
08298    _server_connection.event_sock = accept(lsock3, (struct sockaddr *) &bind_addr, (int *)&size);
08299 
08300    if (_server_connection.send_sock == -1 ||
08301        _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
08302       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08303       return RPC_NET_ERROR;
08304    }
08305 
08306    closesocket(lsock1);
08307    closesocket(lsock2);
08308    closesocket(lsock3);
08309 
08310    /* set TCP_NODELAY option for better performance */
08311 #ifdef OS_VXWORKS
08312    flag = 1;
08313    setsockopt(_server_connection.send_sock,
08314               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08315    setsockopt(_server_connection.event_sock,
08316               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08317 #endif
08318 
08319    /* increase send buffer size to 64kB */
08320    flag = 0x10000;
08321    setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF,
08322               (char *) &flag, sizeof(flag));
08323 
08324    /* send local computer info */
08325    rpc_get_name(local_prog_name);
08326    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08327    sprintf(str, "%d %s", hw_type, local_prog_name);
08328 
08329    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08330 
08331    /* receive remote computer info */
08332    i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
08333    if (i <= 0) {
08334       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
08335       return RPC_NET_ERROR;
08336    }
08337 
08338    sscanf(str, "%d", &remote_hw_type);
08339    _server_connection.remote_hw_type = remote_hw_type;
08340 
08341    /* set dispatcher which receives database updates */
08342    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection,
08343                            (int (*)(void)) rpc_client_dispatch);
08344 
08345    return RPC_SUCCESS;
08346 }
08347 
08348 
08349 /********************************************************************/
08350 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08351 /********************************************************************\
08352 
08353   Routine: rpc_client_disconnect
08354 
08355   Purpose: Close a rpc connection to a MIDAS client
08356 
08357   Input:
08358     HNDLE  hConn           Handle of connection
08359     BOOL   bShutdown       Shut down remote server if TRUE
08360 
08361   Output:
08362     none
08363 
08364   Function value:
08365    RPC_SUCCESS             Successful completion
08366 
08367 \********************************************************************/
08368 {
08369    INT i;
08370 
08371    if (hConn == -1) {
08372       /* close all open connections */
08373       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
08374          if (_client_connection[i].send_sock != 0)
08375             rpc_client_disconnect(i + 1, FALSE);
08376 
08377       /* close server connection from other clients */
08378       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08379          if (_server_acception[i].recv_sock) {
08380             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
08381             closesocket(_server_acception[i].recv_sock);
08382          }
08383    } else {
08384       /* notify server about exit */
08385 
08386       /* set FTCP mode (helps for rebooted VxWorks nodes) */
08387       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
08388       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
08389 
08390       /* close socket */
08391       if (_client_connection[hConn - 1].send_sock)
08392          closesocket(_client_connection[hConn - 1].send_sock);
08393 
08394       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
08395    }
08396 
08397    return RPC_SUCCESS;
08398 }
08399 
08400 
08401 /********************************************************************/
08402 INT rpc_server_disconnect()
08403 /********************************************************************\
08404 
08405   Routine: rpc_server_disconnect
08406 
08407   Purpose: Close a rpc connection to a MIDAS server and close all
08408            server connections from other clients
08409 
08410   Input:
08411     none
08412 
08413   Output:
08414     none
08415 
08416   Function value:
08417    RPC_SUCCESS             Successful completion
08418    RPC_NET_ERROR           Error in socket call
08419    RPC_NO_CONNECTION       Maximum number of connections reached
08420 
08421 \********************************************************************/
08422 {
08423    static int rpc_server_disconnect_recursion_level = 0;
08424 
08425    if (rpc_server_disconnect_recursion_level)
08426       return RPC_SUCCESS;
08427 
08428    rpc_server_disconnect_recursion_level = 1;
08429 
08430    /* flush remaining events */
08431    rpc_flush_event();
08432 
08433    /* notify server about exit */
08434    rpc_call(RPC_ID_EXIT);
08435 
08436    /* close sockets */
08437    closesocket(_server_connection.send_sock);
08438    closesocket(_server_connection.recv_sock);
08439    closesocket(_server_connection.event_sock);
08440 
08441    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
08442 
08443    rpc_server_disconnect_recursion_level = 0;
08444    return RPC_SUCCESS;
08445 }
08446 
08447 
08448 /********************************************************************/
08449 INT rpc_is_remote(void)
08450 /********************************************************************\
08451 
08452   Routine: rpc_is_remote
08453 
08454   Purpose: Return true if program is connected to a remote server
08455 
08456   Input:
08457    none
08458 
08459   Output:
08460     none
08461 
08462   Function value:
08463     INT    RPC connection index
08464 
08465 \********************************************************************/
08466 {
08467    return _server_connection.send_sock != 0;
08468 }
08469 
08470 
08471 /********************************************************************/
08472 INT rpc_get_server_acception(void)
08473 /********************************************************************\
08474 
08475   Routine: rpc_get_server_acception
08476 
08477   Purpose: Return actual RPC server connection index
08478 
08479   Input:
08480    none
08481 
08482   Output:
08483     none
08484 
08485   Function value:
08486     INT    RPC server connection index
08487 
08488 \********************************************************************/
08489 {
08490    return _server_acception_index;
08491 }
08492 
08493 
08494 /********************************************************************/
08495 INT rpc_set_server_acception(INT index)
08496 /********************************************************************\
08497 
08498   Routine: rpc_set_server_acception
08499 
08500   Purpose: Set actual RPC server connection index
08501 
08502   Input:
08503     INT  index              Server index
08504 
08505   Output:
08506     none
08507 
08508   Function value:
08509     RPC_SUCCESS             Successful completion
08510 
08511 \********************************************************************/
08512 {
08513    _server_acception_index = index;
08514    return RPC_SUCCESS;
08515 }
08516 
08517 
08518 /********************************************************************/
08519 INT rpc_get_option(HNDLE hConn, INT item)
08520 /********************************************************************\
08521 
08522   Routine: rpc_get_option
08523 
08524   Purpose: Get actual RPC option
08525 
08526   Input:
08527     HNDLE hConn             RPC connection handle
08528     INT   item              One of RPC_Oxxx
08529 
08530   Output:
08531     none
08532 
08533   Function value:
08534     INT                     Actual option
08535 
08536 \********************************************************************/
08537 {
08538    switch (item) {
08539    case RPC_OTIMEOUT:
08540       if (hConn == -1)
08541          return _server_connection.rpc_timeout;
08542       return _client_connection[hConn - 1].rpc_timeout;
08543 
08544    case RPC_OTRANSPORT:
08545       if (hConn == -1)
08546          return _server_connection.transport;
08547       return _client_connection[hConn - 1].transport;
08548 
08549    case RPC_OHW_TYPE:
08550       {
08551          INT tmp_type, size;
08552          DWORD dummy;
08553          unsigned char *p;
08554          float f;
08555          double d;
08556 
08557          tmp_type = 0;
08558 
08559          /* test pointer size */
08560          size = sizeof(p);
08561          if (size == 2)
08562             tmp_type |= DRI_16;
08563          if (size == 4)
08564             tmp_type |= DRI_32;
08565          if (size == 8)
08566             tmp_type |= DRI_64;
08567 
08568          /* test if little or big endian machine */
08569          dummy = 0x12345678;
08570          p = (unsigned char *) &dummy;
08571          if (*p == 0x78)
08572             tmp_type |= DRI_LITTLE_ENDIAN;
08573          else if (*p == 0x12)
08574             tmp_type |= DRI_BIG_ENDIAN;
08575          else
08576             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
08577 
08578          /* floating point format */
08579          f = (float) 1.2345;
08580          dummy = 0;
08581          memcpy(&dummy, &f, sizeof(f));
08582          if ((dummy & 0xFF) == 0x19 &&
08583              ((dummy >> 8) & 0xFF) == 0x04 &&
08584              ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
08585             tmp_type |= DRF_IEEE;
08586          else if ((dummy & 0xFF) == 0x9E &&
08587                   ((dummy >> 8) & 0xFF) == 0x40 &&
08588                   ((dummy >> 16) & 0xFF) == 0x19 && ((dummy >> 24) & 0xFF) == 0x04)
08589             tmp_type |= DRF_G_FLOAT;
08590          else
08591             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08592 
08593          d = (double) 1.2345;
08594          dummy = 0;
08595          memcpy(&dummy, &d, sizeof(f));
08596          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
08597              ((dummy >> 8) & 0xFF) == 0x97 &&
08598              ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
08599             tmp_type |= DRF_IEEE;
08600          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
08601                   ((dummy >> 8) & 0xFF) == 0xC0 &&
08602                   ((dummy >> 16) & 0xFF) == 0xF3 && ((dummy >> 24) & 0xFF) == 0x3F)
08603             tmp_type |= DRF_IEEE;
08604          else if ((dummy & 0xFF) == 0x13 &&
08605                   ((dummy >> 8) & 0xFF) == 0x40 &&
08606                   ((dummy >> 16) & 0xFF) == 0x83 && ((dummy >> 24) & 0xFF) == 0xC0)
08607             tmp_type |= DRF_G_FLOAT;
08608          else if ((dummy & 0xFF) == 0x9E &&
08609                   ((dummy >> 8) & 0xFF) == 0x40 &&
08610                   ((dummy >> 16) & 0xFF) == 0x18 && ((dummy >> 24) & 0xFF) == 0x04)
08611             cm_msg(MERROR, "rpc_get_option",
08612                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
08613          else
08614             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08615 
08616          return tmp_type;
08617       }
08618 
08619    default:
08620       cm_msg(MERROR, "rpc_get_option", "invalid argument");
08621       break;
08622    }
08623 
08624    return 0;
08625 }
08626 
08627 /**dox***************************************************************/
08628 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08629 
08630 /********************************************************************/
08631 /**
08632 Set RPC option
08633 @param hConn              RPC connection handle
08634 @param item               One of RPC_Oxxx
08635 @param value              Value to set
08636 @return RPC_SUCCESS
08637 */
08638 INT rpc_set_option(HNDLE hConn, INT item, INT value)
08639 {
08640    switch (item) {
08641    case RPC_OTIMEOUT:
08642       if (hConn == -1)
08643          _server_connection.rpc_timeout = value;
08644       else
08645          _client_connection[hConn - 1].rpc_timeout = value;
08646       break;
08647 
08648    case RPC_OTRANSPORT:
08649       if (hConn == -1)
08650          _server_connection.transport = value;
08651       else
08652          _client_connection[hConn - 1].transport = value;
08653       break;
08654 
08655    case RPC_NODELAY:
08656       if (hConn == -1)
08657          setsockopt(_server_connection.send_sock, IPPROTO_TCP,
08658                     TCP_NODELAY, (char *) &value, sizeof(value));
08659       else
08660          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP,
08661                     TCP_NODELAY, (char *) &value, sizeof(value));
08662       break;
08663 
08664    default:
08665       cm_msg(MERROR, "rpc_set_option", "invalid argument");
08666       break;
08667    }
08668 
08669    return 0;
08670 }
08671 
08672 
08673 /**dox***************************************************************/
08674 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08675 
08676 /********************************************************************/
08677 POINTER_T rpc_get_server_option(INT item)
08678 /********************************************************************\
08679 
08680   Routine: rpc_get_server_option
08681 
08682   Purpose: Get actual RPC option for server connection
08683 
08684   Input:
08685     INT  item               One of RPC_Oxxx
08686 
08687   Output:
08688     none
08689 
08690   Function value:
08691     INT                     Actual option
08692 
08693 \********************************************************************/
08694 {
08695    INT i;
08696 
08697    if (item == RPC_OSERVER_TYPE)
08698       return _server_type;
08699 
08700    if (item == RPC_OSERVER_NAME)
08701       return (POINTER_T) _server_name;
08702 
08703    /* return 0 for local calls */
08704    if (_server_type == ST_NONE)
08705       return 0;
08706 
08707    /* check which connections belongs to caller */
08708    if (_server_type == ST_MTHREAD) {
08709       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08710          if (_server_acception[i].tid == ss_gettid())
08711             break;
08712    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08713       i = MAX(0, _server_acception_index - 1);
08714    else
08715       i = 0;
08716 
08717    switch (item) {
08718    case RPC_CONVERT_FLAGS:
08719       return _server_acception[i].convert_flags;
08720    case RPC_ODB_HANDLE:
08721       return _server_acception[i].odb_handle;
08722    case RPC_CLIENT_HANDLE:
08723       return _server_acception[i].client_handle;
08724    case RPC_SEND_SOCK:
08725       return _server_acception[i].send_sock;
08726    case RPC_WATCHDOG_TIMEOUT:
08727       return _server_acception[i].watchdog_timeout;
08728    }
08729 
08730    return 0;
08731 }
08732 
08733 
08734 /********************************************************************/
08735 INT rpc_set_server_option(INT item, POINTER_T value)
08736 /********************************************************************\
08737 
08738   Routine: rpc_set_server_option
08739 
08740   Purpose: Set RPC option for server connection
08741 
08742   Input:
08743    INT  item               One of RPC_Oxxx
08744    INT  value              Value to set
08745 
08746   Output:
08747     none
08748 
08749   Function value:
08750     RPC_SUCCESS             Successful completion
08751 
08752 \********************************************************************/
08753 {
08754    INT i;
08755 
08756    if (item == RPC_OSERVER_TYPE) {
08757       _server_type = value;
08758       return RPC_SUCCESS;
08759    }
08760    if (item == RPC_OSERVER_NAME) {
08761       strcpy(_server_name, (char *) value);
08762       return RPC_SUCCESS;
08763    }
08764 
08765    /* check which connections belongs to caller */
08766    if (_server_type == ST_MTHREAD) {
08767       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08768          if (_server_acception[i].tid == ss_gettid())
08769             break;
08770    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08771       i = MAX(0, _server_acception_index - 1);
08772    else
08773       i = 0;
08774 
08775    switch (item) {
08776    case RPC_CONVERT_FLAGS:
08777       _server_acception[i].convert_flags = value;
08778       break;
08779    case RPC_ODB_HANDLE:
08780       _server_acception[i].odb_handle = value;
08781       break;
08782    case RPC_CLIENT_HANDLE:
08783       _server_acception[i].client_handle = value;
08784       break;
08785    case RPC_WATCHDOG_TIMEOUT:
08786       _server_acception[i].watchdog_timeout = value;
08787       break;
08788    }
08789 
08790    return RPC_SUCCESS;
08791 }
08792 
08793 
08794 /********************************************************************/
08795 INT rpc_get_name(char *name)
08796 /********************************************************************\
08797 
08798   Routine: rpc_get_name
08799 
08800   Purpose: Get name set by rpc_set_name
08801 
08802   Input:
08803     none
08804 
08805   Output:
08806     char*  name             The location pointed by *name receives a
08807                             copy of the _prog_name
08808 
08809   Function value:
08810     RPC_SUCCESS             Successful completion
08811 
08812 \********************************************************************/
08813 {
08814    strcpy(name, _client_name);
08815 
08816    return RPC_SUCCESS;
08817 }
08818 
08819 
08820 /********************************************************************/
08821 INT rpc_set_name(char *name)
08822 /********************************************************************\
08823 
08824   Routine: rpc_set_name
08825 
08826   Purpose: Set name of actual program for further rpc connections
08827 
08828   Input:
08829    char *name               Program name, up to NAME_LENGTH chars,
08830                             no blanks
08831 
08832   Output:
08833     none
08834 
08835   Function value:
08836     RPC_SUCCESS             Successful completion
08837 
08838 \********************************************************************/
08839 {
08840    strcpy(_client_name, name);
08841 
08842    return RPC_SUCCESS;
08843 }
08844 
08845 
08846 /********************************************************************/
08847 INT rpc_set_debug(void (*func) (char *), INT mode)
08848 /********************************************************************\
08849 
08850   Routine: rpc_set_debug
08851 
08852   Purpose: Set a function which is called on every RPC call to
08853            display the function name and parameters of the RPC
08854            call.
08855 
08856   Input:
08857    void *func(char*)        Pointer to function.
08858    INT  mode                Debug mode
08859 
08860   Output:
08861     none
08862 
08863   Function value:
08864     RPC_SUCCESS             Successful completion
08865 
08866 \********************************************************************/
08867 {
08868    _debug_print = func;
08869    _debug_mode = mode;
08870    return RPC_SUCCESS;
08871 }
08872 
08873 /********************************************************************/
08874 void rpc_debug_printf(char *format, ...)
08875 /********************************************************************\
08876 
08877   Routine: rpc_debug_print
08878 
08879   Purpose: Calls function set via rpc_set_debug to output a string.
08880 
08881   Input:
08882    char *str                Debug string
08883 
08884   Output:
08885     none
08886 
08887 \********************************************************************/
08888 {
08889    va_list argptr;
08890    char str[1000];
08891 
08892    if (_debug_mode) {
08893       va_start(argptr, format);
08894       vsprintf(str, (char *) format, argptr);
08895       va_end(argptr);
08896 
08897       if (_debug_print) {
08898          strcat(str, "\n");
08899          _debug_print(str);
08900       } else
08901          puts(str);
08902    }
08903 }
08904 
08905 /********************************************************************/
08906 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
08907 {
08908    switch (arg_type) {
08909       /* On the stack, the minimum parameter size is sizeof(int).
08910          To avoid problems on little endian systems, treat all
08911          smaller parameters as int's */
08912    case TID_BYTE:
08913    case TID_SBYTE:
08914    case TID_CHAR:
08915    case TID_WORD:
08916    case TID_SHORT:
08917       *((int *) arg) = va_arg(*arg_ptr, int);
08918       break;
08919 
08920    case TID_INT:
08921    case TID_BOOL:
08922       *((INT *) arg) = va_arg(*arg_ptr, INT);
08923       break;
08924 
08925    case TID_DWORD:
08926       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
08927       break;
08928 
08929       /* float variables are passed as double by the compiler */
08930    case TID_FLOAT:
08931       *((float *) arg) = (float) va_arg(*arg_ptr, double);
08932       break;
08933 
08934    case TID_DOUBLE:
08935       *((double *) arg) = va_arg(*arg_ptr, double);
08936       break;
08937 
08938    case TID_ARRAY:
08939       *((char **) arg) = va_arg(*arg_ptr, char *);
08940       break;
08941    }
08942 }
08943 
08944 
08945 /********************************************************************/
08946 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
08947 /********************************************************************\
08948 
08949   Routine: rpc_client_call
08950 
08951   Purpose: Call a function on a MIDAS client
08952 
08953   Input:
08954     INT  hConn              Client connection
08955     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
08956 
08957     ...                     variable argument list
08958 
08959   Output:
08960     (depends on argument list)
08961 
08962   Function value:
08963     RPC_SUCCESS             Successful completion
08964     RPC_NET_ERROR           Error in socket call
08965     RPC_NO_CONNECTION       No active connection
08966     RPC_TIMEOUT             Timeout in RPC call
08967     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
08968     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
08969 
08970 \********************************************************************/
08971 {
08972    va_list ap, aptmp;
08973    char arg[8], arg_tmp[8];
08974    INT arg_type, transport, rpc_timeout;
08975    INT i, index, status, rpc_index;
08976    INT param_size, arg_size, send_size;
08977    INT tid, flags;
08978    fd_set readfds;
08979    struct timeval timeout;
08980    char *param_ptr, str[80];
08981    BOOL bpointer, bbig;
08982    NET_COMMAND *nc;
08983    int send_sock;
08984 
08985    index = hConn - 1;
08986 
08987    if (_client_connection[index].send_sock == 0) {
08988       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
08989       return RPC_NO_CONNECTION;
08990    }
08991 
08992    send_sock = _client_connection[index].send_sock;
08993    rpc_timeout = _client_connection[index].rpc_timeout;
08994    transport = _client_connection[index].transport;
08995 
08996    /* init network buffer */
08997    if (_net_send_buffer_size == 0) {
08998       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
08999       if (_net_send_buffer == NULL) {
09000          cm_msg(MERROR, "rpc_client_call",
09001                 "not enough memory to allocate network buffer");
09002          return RPC_EXCEED_BUFFER;
09003       }
09004       _net_send_buffer_size = NET_BUFFER_SIZE;
09005    }
09006 
09007    nc = (NET_COMMAND *) _net_send_buffer;
09008    nc->header.routine_id = routine_id;
09009 
09010    if (transport == RPC_FTCP)
09011       nc->header.routine_id |= TCP_FAST;
09012 
09013    for (i = 0;; i++)
09014       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09015          break;
09016    rpc_index = i;
09017    if (rpc_list[i].id == 0) {
09018       sprintf(str, "invalid rpc ID (%d)", routine_id);
09019       cm_msg(MERROR, "rpc_client_call", str);
09020       return RPC_INVALID_ID;
09021    }
09022 
09023    /* examine variable argument list and convert it to parameter array */
09024    va_start(ap, routine_id);
09025 
09026    /* find out if we are on a big endian system */
09027    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09028 
09029    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09030       tid = rpc_list[rpc_index].param[i].tid;
09031       flags = rpc_list[rpc_index].param[i].flags;
09032 
09033       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09034           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09035           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09036 
09037       if (bpointer)
09038          arg_type = TID_ARRAY;
09039       else
09040          arg_type = tid;
09041 
09042       /* floats are passed as doubles, at least under NT */
09043       if (tid == TID_FLOAT && !bpointer)
09044          arg_type = TID_DOUBLE;
09045 
09046       /* get pointer to argument */
09047       rpc_va_arg(&ap, arg_type, arg);
09048 
09049       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09050       if (bbig) {
09051          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09052             arg[0] = arg[3];
09053          }
09054          if (tid == TID_WORD || tid == TID_SHORT) {
09055             arg[0] = arg[2];
09056             arg[1] = arg[3];
09057          }
09058       }
09059 
09060       if (flags & RPC_IN) {
09061          if (bpointer)
09062             arg_size = tid_size[tid];
09063          else
09064             arg_size = tid_size[arg_type];
09065 
09066          /* for strings, the argument size depends on the string length */
09067          if (tid == TID_STRING || tid == TID_LINK)
09068             arg_size = 1 + strlen((char *) *((char **) arg));
09069 
09070          /* for varibale length arrays, the size is given by
09071             the next parameter on the stack */
09072          if (flags & RPC_VARARRAY) {
09073             memcpy(&aptmp, &ap, sizeof(ap));
09074             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09075 
09076             if (flags & RPC_OUT)
09077                arg_size = *((INT *) * ((void **) arg_tmp));
09078             else
09079                arg_size = *((INT *) arg_tmp);
09080 
09081             *((INT *) param_ptr) = ALIGN8(arg_size);
09082             param_ptr += ALIGN8(sizeof(INT));
09083          }
09084 
09085          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09086             arg_size = rpc_list[rpc_index].param[i].n;
09087 
09088          /* always align parameter size */
09089          param_size = ALIGN8(arg_size);
09090 
09091          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > NET_BUFFER_SIZE) {
09092             cm_msg(MERROR, "rpc_client_call",
09093                    "parameters (%d) too large for network buffer (%d)",
09094                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, NET_BUFFER_SIZE);
09095             return RPC_EXCEED_BUFFER;
09096          }
09097 
09098          if (bpointer)
09099             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09100          else {
09101             /* floats are passed as doubles on most systems */
09102             if (tid != TID_FLOAT)
09103                memcpy(param_ptr, arg, arg_size);
09104             else
09105                *((float *) param_ptr) = (float) *((double *) arg);
09106          }
09107 
09108          param_ptr += param_size;
09109 
09110       }
09111    }
09112 
09113    va_end(ap);
09114 
09115    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09116 
09117    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09118 
09119    /* in FAST TCP mode, only send call and return immediately */
09120    if (transport == RPC_FTCP) {
09121       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09122 
09123       if (i != send_size) {
09124          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09125          return RPC_NET_ERROR;
09126       }
09127 
09128       return RPC_SUCCESS;
09129    }
09130 
09131    /* in TCP mode, send and wait for reply on send socket */
09132    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09133    if (i != send_size) {
09134       cm_msg(MERROR, "rpc_client_call",
09135              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09136              rpc_list[rpc_index].name, _client_connection[index].host_name);
09137       return RPC_NET_ERROR;
09138    }
09139 
09140    /* make some timeout checking */
09141    if (rpc_timeout > 0) {
09142       FD_ZERO(&readfds);
09143       FD_SET(send_sock, &readfds);
09144 
09145       timeout.tv_sec = rpc_timeout / 1000;
09146       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09147 
09148       do {
09149          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09150 
09151          /* if an alarm signal was cought, restart select with reduced timeout */
09152          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09153             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09154 
09155       } while (status == -1);   /* dont return if an alarm signal was cought */
09156 
09157       if (!FD_ISSET(send_sock, &readfds)) {
09158          cm_msg(MERROR, "rpc_client_call", "rpc timeout, routine = \"%s\", host = \"%s\"",
09159                 rpc_list[rpc_index].name, _client_connection[index].host_name);
09160 
09161          /* disconnect to avoid that the reply to this rpc_call comes at
09162             the next rpc_call */
09163          rpc_client_disconnect(hConn, FALSE);
09164 
09165          return RPC_TIMEOUT;
09166       }
09167    }
09168 
09169    /* receive result on send socket */
09170    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09171 
09172    if (i <= 0) {
09173       cm_msg(MERROR, "rpc_client_call",
09174              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09175              rpc_list[rpc_index].name, _client_connection[index].host_name);
09176       return RPC_NET_ERROR;
09177    }
09178 
09179    /* extract result variables and place it to argument list */
09180    status = nc->header.routine_id;
09181 
09182    va_start(ap, routine_id);
09183 
09184    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09185       tid = rpc_list[rpc_index].param[i].tid;
09186       flags = rpc_list[rpc_index].param[i].flags;
09187 
09188       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09189           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09190           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09191 
09192       if (bpointer)
09193          arg_type = TID_ARRAY;
09194       else
09195          arg_type = rpc_list[rpc_index].param[i].tid;
09196 
09197       if (tid == TID_FLOAT && !bpointer)
09198          arg_type = TID_DOUBLE;
09199 
09200       rpc_va_arg(&ap, arg_type, arg);
09201 
09202       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09203          tid = rpc_list[rpc_index].param[i].tid;
09204          flags = rpc_list[rpc_index].param[i].flags;
09205 
09206          arg_size = tid_size[tid];
09207 
09208          if (tid == TID_STRING || tid == TID_LINK)
09209             arg_size = strlen((char *) (param_ptr)) + 1;
09210 
09211          if (flags & RPC_VARARRAY) {
09212             arg_size = *((INT *) param_ptr);
09213             param_ptr += ALIGN8(sizeof(INT));
09214          }
09215 
09216          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09217             arg_size = rpc_list[rpc_index].param[i].n;
09218 
09219          /* return parameters are always pointers */
09220          if (*((char **) arg))
09221             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09222 
09223          /* parameter size is always aligned */
09224          param_size = ALIGN8(arg_size);
09225 
09226          param_ptr += param_size;
09227       }
09228    }
09229 
09230    va_end(ap);
09231 
09232    return status;
09233 }
09234 
09235 
09236 /********************************************************************/
09237 INT rpc_call(const INT routine_id, ...)
09238 /********************************************************************\
09239 
09240   Routine: rpc_call
09241 
09242   Purpose: Call a function on a MIDAS server
09243 
09244   Input:
09245     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09246 
09247     ...                     variable argument list
09248 
09249   Output:
09250     (depends on argument list)
09251 
09252   Function value:
09253     RPC_SUCCESS             Successful completion
09254     RPC_NET_ERROR           Error in socket call
09255     RPC_NO_CONNECTION       No active connection
09256     RPC_TIMEOUT             Timeout in RPC call
09257     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09258     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09259 
09260 \********************************************************************/
09261 {
09262    va_list ap, aptmp;
09263    char arg[8], arg_tmp[8];
09264    INT arg_type, transport, rpc_timeout;
09265    INT i, index, status;
09266    INT param_size, arg_size, send_size;
09267    INT tid, flags;
09268    fd_set readfds;
09269    struct timeval timeout;
09270    char *param_ptr, str[80];
09271    BOOL bpointer, bbig;
09272    NET_COMMAND *nc;
09273    int send_sock;
09274 
09275    send_sock = _server_connection.send_sock;
09276    transport = _server_connection.transport;
09277    rpc_timeout = _server_connection.rpc_timeout;
09278 
09279    /* init network buffer */
09280    if (_net_send_buffer_size == 0) {
09281       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09282       if (_net_send_buffer == NULL) {
09283          cm_msg(MERROR, "rpc_call", "not enough memory to allocate network buffer");
09284          return RPC_EXCEED_BUFFER;
09285       }
09286       _net_send_buffer_size = NET_BUFFER_SIZE;
09287    }
09288 
09289    nc = (NET_COMMAND *) _net_send_buffer;
09290    nc->header.routine_id = routine_id;
09291 
09292    if (transport == RPC_FTCP)
09293       nc->header.routine_id |= TCP_FAST;
09294 
09295    for (i = 0;; i++)
09296       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09297          break;
09298    index = i;
09299    if (rpc_list[i].id == 0) {
09300       sprintf(str, "invalid rpc ID (%d)", routine_id);
09301       cm_msg(MERROR, "rpc_call", str);
09302       return RPC_INVALID_ID;
09303    }
09304 
09305    /* examine variable argument list and convert it to parameter array */
09306    va_start(ap, routine_id);
09307 
09308    /* find out if we are on a big endian system */
09309    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09310 
09311    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09312       tid = rpc_list[index].param[i].tid;
09313       flags = rpc_list[index].param[i].flags;
09314 
09315       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09316           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09317           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09318 
09319       if (bpointer)
09320          arg_type = TID_ARRAY;
09321       else
09322          arg_type = tid;
09323 
09324       /* floats are passed as doubles, at least under NT */
09325       if (tid == TID_FLOAT && !bpointer)
09326          arg_type = TID_DOUBLE;
09327 
09328       /* get pointer to argument */
09329       rpc_va_arg(&ap, arg_type, arg);
09330 
09331       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09332       if (bbig) {
09333          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09334             arg[0] = arg[3];
09335          }
09336          if (tid == TID_WORD || tid == TID_SHORT) {
09337             arg[0] = arg[2];
09338             arg[1] = arg[3];
09339          }
09340       }
09341 
09342       if (flags & RPC_IN) {
09343          if (bpointer)
09344             arg_size = tid_size[tid];
09345          else
09346             arg_size = tid_size[arg_type];
09347 
09348          /* for strings, the argument size depends on the string length */
09349          if (tid == TID_STRING || tid == TID_LINK)
09350             arg_size = 1 + strlen((char *) *((char **) arg));
09351 
09352          /* for varibale length arrays, the size is given by
09353             the next parameter on the stack */
09354          if (flags & RPC_VARARRAY) {
09355             memcpy(&aptmp, &ap, sizeof(ap));
09356             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09357 
09358             if (flags & RPC_OUT)
09359                arg_size = *((INT *) * ((void **) arg_tmp));
09360             else
09361                arg_size = *((INT *) arg_tmp);
09362 
09363             *((INT *) param_ptr) = ALIGN8(arg_size);
09364             param_ptr += ALIGN8(sizeof(INT));
09365          }
09366 
09367          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09368             arg_size = rpc_list[index].param[i].n;
09369 
09370          /* always align parameter size */
09371          param_size = ALIGN8(arg_size);
09372 
09373          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > NET_BUFFER_SIZE) {
09374             cm_msg(MERROR, "rpc_call",
09375                    "parameters (%d) too large for network buffer (%d)",
09376                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, NET_BUFFER_SIZE);
09377             return RPC_EXCEED_BUFFER;
09378          }
09379 
09380          if (bpointer)
09381             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09382          else {
09383             /* floats are passed as doubles on most systems */
09384             if (tid != TID_FLOAT)
09385                memcpy(param_ptr, arg, arg_size);
09386             else
09387                *((float *) param_ptr) = (float) *((double *) arg);
09388          }
09389 
09390          param_ptr += param_size;
09391 
09392       }
09393    }
09394 
09395    va_end(ap);
09396 
09397    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09398 
09399    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09400 
09401    /* in FAST TCP mode, only send call and return immediately */
09402    if (transport == RPC_FTCP) {
09403       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09404 
09405       if (i != send_size) {
09406          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09407          return RPC_NET_ERROR;
09408       }
09409 
09410       return RPC_SUCCESS;
09411    }
09412 
09413    /* in TCP mode, send and wait for reply on send socket */
09414    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09415    if (i != send_size) {
09416       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09417       return RPC_NET_ERROR;
09418    }
09419 
09420    /* make some timeout checking */
09421    if (rpc_timeout > 0) {
09422       FD_ZERO(&readfds);
09423       FD_SET(send_sock, &readfds);
09424 
09425       timeout.tv_sec = rpc_timeout / 1000;
09426       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09427 
09428       do {
09429          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09430 
09431          /* if an alarm signal was cought, restart select with reduced timeout */
09432          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09433             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09434 
09435       } while (status == -1);   /* dont return if an alarm signal was cought */
09436 
09437       if (!FD_ISSET(send_sock, &readfds)) {
09438          cm_msg(MERROR, "rpc_call", "rpc timeout, routine = \"%s\"",
09439                 rpc_list[index].name);
09440 
09441          /* disconnect to avoid that the reply to this rpc_call comes at
09442             the next rpc_call */
09443          rpc_server_disconnect();
09444 
09445          return RPC_TIMEOUT;
09446       }
09447    }
09448 
09449    /* receive result on send socket */
09450    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09451 
09452    if (i <= 0) {
09453       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"",
09454              rpc_list[index].name);
09455       return RPC_NET_ERROR;
09456    }
09457 
09458    /* extract result variables and place it to argument list */
09459    status = nc->header.routine_id;
09460 
09461    va_start(ap, routine_id);
09462 
09463    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09464       tid = rpc_list[index].param[i].tid;
09465       flags = rpc_list[index].param[i].flags;
09466 
09467       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09468           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09469           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09470 
09471       if (bpointer)
09472          arg_type = TID_ARRAY;
09473       else
09474          arg_type = rpc_list[index].param[i].tid;
09475 
09476       if (tid == TID_FLOAT && !bpointer)
09477          arg_type = TID_DOUBLE;
09478 
09479       rpc_va_arg(&ap, arg_type, arg);
09480 
09481       if (rpc_list[index].param[i].flags & RPC_OUT) {
09482          tid = rpc_list[index].param[i].tid;
09483          arg_size = tid_size[tid];
09484 
09485          if (tid == TID_STRING || tid == TID_LINK)
09486             arg_size = strlen((char *) (param_ptr)) + 1;
09487 
09488          if (flags & RPC_VARARRAY) {
09489             arg_size = *((INT *) param_ptr);
09490             param_ptr += ALIGN8(sizeof(INT));
09491          }
09492 
09493          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09494             arg_size = rpc_list[index].param[i].n;
09495 
09496          /* return parameters are always pointers */
09497          if (*((char **) arg))
09498             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09499 
09500          /* parameter size is always aligned */
09501          param_size = ALIGN8(arg_size);
09502 
09503          param_ptr += param_size;
09504       }
09505    }
09506 
09507    va_end(ap);
09508 
09509    return status;
09510 }
09511 
09512 
09513 /********************************************************************/
09514 INT rpc_set_opt_tcp_size(INT tcp_size)
09515 {
09516    INT old;
09517 
09518    old = _opt_tcp_size;
09519    _opt_tcp_size = tcp_size;
09520    return old;
09521 }
09522 
09523 INT rpc_get_opt_tcp_size()
09524 {
09525    return _opt_tcp_size;
09526 }
09527 
09528 /**dox***************************************************************/
09529 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09530 
09531 /********************************************************************/
09532 /**
09533 Fast send_event routine which bypasses the RPC layer and
09534            sends the event directly at the TCP level.
09535 @param buffer_handle      Handle of the buffer to send the event to.
09536                             Must be obtained via bm_open_buffer.
09537 @param source            Address of the event to send. It must have
09538                             a proper event header.
09539 @param buf_size           Size of event in bytes with header.
09540 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
09541                             function returns immediately if it cannot
09542                             send the event over the network. In SYNC
09543                             mode, it waits until the packet is sent
09544                             (blocking).
09545 
09546 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR, 
09547         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER       
09548 */
09549 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
09550 {
09551    INT i;
09552    NET_COMMAND *nc;
09553    unsigned long flag;
09554    BOOL would_block = 0;
09555    DWORD aligned_buf_size;
09556 
09557    aligned_buf_size = ALIGN8(buf_size);
09558 
09559    if (aligned_buf_size !=
09560        (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
09561       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
09562       return BM_INVALID_PARAM;
09563    }
09564    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
09565       cm_msg(MERROR, "rpc_send_event",
09566              "event size (%d) larger than maximum event size (%d)",
09567              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
09568       return RPC_EXCEED_BUFFER;
09569    }
09570 
09571    if (!rpc_is_remote())
09572       return bm_send_event(buffer_handle, source, buf_size, async_flag);
09573 
09574    /* init network buffer */
09575    if (!_tcp_buffer)
09576       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
09577    if (!_tcp_buffer) {
09578       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
09579       return RPC_EXCEED_BUFFER;
09580    }
09581 
09582    /* check if not enough space in TCP buffer */
09583    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
09584        (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
09585       /* set socket to nonblocking IO */
09586       if (async_flag == ASYNC) {
09587          flag = 1;
09588 #ifdef OS_VXWORKS
09589          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
09590 #else
09591          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
09592 #endif
09593       }
09594 
09595       i = send_tcp(_server_connection.send_sock,
09596                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09597 
09598       if (i < 0)
09599 #ifdef OS_WINNT
09600          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
09601 #else
09602          would_block = (errno == EWOULDBLOCK);
09603 #endif
09604 
09605       /* set socket back to blocking IO */
09606       if (async_flag == ASYNC) {
09607          flag = 0;
09608 #ifdef OS_VXWORKS
09609          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
09610 #else
09611          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
09612 #endif
09613       }
09614 
09615       /* increment read pointer */
09616       if (i > 0)
09617          _tcp_rp += i;
09618 
09619       /* check if whole buffer is sent */
09620       if (_tcp_rp == _tcp_wp)
09621          _tcp_rp = _tcp_wp = 0;
09622 
09623       if (i < 0 && !would_block) {
09624          printf("send_tcp() returned %d\n", i);
09625          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed");
09626          return RPC_NET_ERROR;
09627       }
09628 
09629       /* return if buffer is not emptied */
09630       if (_tcp_wp > 0)
09631          return BM_ASYNC_RETURN;
09632    }
09633 
09634    nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
09635    nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
09636    nc->header.param_size = 4 * 8 + aligned_buf_size;
09637 
09638    /* assemble parameters manually */
09639    *((INT *) (&nc->param[0])) = buffer_handle;
09640    *((INT *) (&nc->param[8])) = buf_size;
09641 
09642    /* send events larger than optimal buffer size directly */
09643    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
09644       /* send header */
09645       send_tcp(_server_connection.send_sock,
09646                _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
09647 
09648       /* send data */
09649       send_tcp(_server_connection.send_sock, (char *) source, aligned_buf_size, 0);
09650 
09651       /* send last two parameters */
09652       *((INT *) (&nc->param[0])) = buf_size;
09653       *((INT *) (&nc->param[8])) = 0;
09654       send_tcp(_server_connection.send_sock, &nc->param[0], 16, 0);
09655    } else {
09656       /* copy event */
09657       memcpy(&nc->param[16], source, buf_size);
09658 
09659       /* last two parameters (buf_size and async_flag */
09660       *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
09661       *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
09662 
09663       _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09664    }
09665 
09666    return RPC_SUCCESS;
09667 }
09668 
09669 
09670 
09671 /**dox***************************************************************/
09672 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09673 
09674 /********************************************************************/
09675 int rpc_get_send_sock()
09676 /********************************************************************\
09677 
09678   Routine: rpc_get_send_sock
09679 
09680   Purpose: Return send socket to MIDAS server. Used by MFE.C for
09681            optimized event sending.
09682 
09683   Input:
09684     none
09685 
09686   Output:
09687     none
09688 
09689   Function value:
09690     int    socket
09691 
09692 \********************************************************************/
09693 {
09694    return _server_connection.send_sock;
09695 }
09696 
09697 
09698 /********************************************************************/
09699 int rpc_get_event_sock()
09700 /********************************************************************\
09701 
09702   Routine: rpc_get_event_sock
09703 
09704   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
09705            optimized event sending.
09706 
09707   Input:
09708     none
09709 
09710   Output:
09711     none
09712 
09713   Function value:
09714     int    socket
09715 
09716 \********************************************************************/
09717 {
09718    return _server_connection.event_sock;
09719 }
09720 
09721 /**dox***************************************************************/
09722 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09723 
09724 /********************************************************************/
09725 /**
09726 Send event residing in the TCP cache buffer filled by
09727            rpc_send_event. This routine should be called when a
09728            run is stopped.
09729 
09730 @return RPC_SUCCESS, RPC_NET_ERROR
09731 */
09732 INT rpc_flush_event()
09733 {
09734    INT i;
09735 
09736    if (!rpc_is_remote())
09737       return RPC_SUCCESS;
09738 
09739    /* return if rpc_send_event was not called */
09740    if (!_tcp_buffer || _tcp_wp == 0)
09741       return RPC_SUCCESS;
09742 
09743    /* empty TCP buffer */
09744    if (_tcp_wp > 0) {
09745       i = send_tcp(_server_connection.send_sock,
09746                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09747 
09748       if (i != _tcp_wp - _tcp_rp) {
09749          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
09750          return RPC_NET_ERROR;
09751       }
09752    }
09753 
09754    _tcp_rp = _tcp_wp = 0;
09755 
09756    return RPC_SUCCESS;
09757 }
09758 
09759 /**dox***************************************************************/
09760 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09761 
09762 /********************************************************************/
09763 
09764 typedef struct {
09765    int transition;
09766    int run_number;
09767    time_t trans_time;
09768    int sequence_number;
09769 } TR_FIFO;
09770 
09771 static TR_FIFO tr_fifo[10];
09772 static int trf_wp, trf_rp;
09773 
09774 static INT rpc_transition_dispatch(INT index, void *prpc_param[])
09775 /********************************************************************\
09776 
09777   Routine: rpc_transition_dispatch
09778 
09779   Purpose: Gets called when a transition function was registered and
09780            a transition occured. Internal use only.
09781 
09782   Input:
09783     INT    index            RPC function ID
09784     void   *prpc_param      RPC parameters
09785 
09786   Output:
09787     none
09788 
09789   Function value:
09790     INT    return value from called user routine
09791 
09792 \********************************************************************/
09793 {
09794    INT status, i;
09795 
09796    /* erase error string */
09797    *(CSTRING(2)) = 0;
09798 
09799    if (index == RPC_RC_TRANSITION) {
09800       for (i = 0; i < MAX_TRANSITIONS; i++)
09801          if (_trans_table[i].transition == CINT(0) &&
09802              _trans_table[i].sequence_number == CINT(4))
09803             break;
09804 
09805       /* call registerd function */
09806       if (i < MAX_TRANSITIONS) {
09807          if (_trans_table[i].func)
09808             /* execute callback if defined */
09809             status = _trans_table[i].func(CINT(1), CSTRING(2));
09810          else {
09811             /* store transition in FIFO */
09812             tr_fifo[trf_wp].transition = CINT(0);
09813             tr_fifo[trf_wp].run_number = CINT(1);
09814             tr_fifo[trf_wp].trans_time = time(NULL);
09815             tr_fifo[trf_wp].sequence_number = CINT(4);
09816             trf_wp = (trf_wp + 1) % 10;
09817             status = RPC_SUCCESS;
09818          }
09819       } else
09820          status = RPC_SUCCESS;
09821 
09822    } else {
09823       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
09824       status = RPC_INVALID_ID;
09825    }
09826 
09827    return status;
09828 }
09829 
09830 /********************************************************************/
09831 int cm_query_transition(int *transition, int *run_number, int *trans_time)
09832 /********************************************************************\
09833 
09834   Routine: cm_query_transition
09835 
09836   Purpose: Query system if transition has occured. Normally, one 
09837            registers callbacks for transitions via 
09838            cm_register_transition. In some environments however,
09839            callbacks are not possible. In that case one spciefies
09840            a NULL pointer as the callback routine and can query
09841            transitions "manually" by calling this functions. A small
09842            FIFO takes care that no transition is lost if this functions
09843            did not get called between some transitions.
09844 
09845   Output:
09846     INT   *transition        Type of transition, one of TR_xxx
09847     INT   *run_nuber         Run number for transition
09848     time_t *trans_time       Time (in UNIX time) of transition
09849 
09850   Function value:
09851     FALSE  No transition occured since last call
09852     TRUE   Transition occured
09853 
09854 \********************************************************************/
09855 {
09856 
09857    if (trf_wp == trf_rp)
09858       return FALSE;
09859 
09860    if (transition)
09861       *transition = tr_fifo[trf_rp].transition;
09862 
09863    if (run_number)
09864       *run_number = tr_fifo[trf_rp].run_number;
09865 
09866    if (trans_time)
09867       *trans_time = (int) tr_fifo[trf_rp].trans_time;
09868 
09869    trf_rp = (trf_rp + 1) % 10;
09870 
09871    return TRUE;
09872 }
09873 
09874 /********************************************************************\
09875 *                        server functions                            *
09876 \********************************************************************/
09877 
09878 
09879 /********************************************************************/
09880 INT recv_tcp_server(INT index, char *buffer, DWORD buffer_size, INT flags,
09881                     INT * remaining)
09882 /********************************************************************\
09883 
09884   Routine: recv_tcp_server
09885 
09886   Purpose: TCP receive routine with local cache. To speed up network
09887            performance, a 64k buffer is read in at once and split into
09888            several RPC command on successive calls to recv_tcp_server.
09889            Therefore, the number of recv() calls is minimized.
09890 
09891            This routine is ment to be called by the server process.
09892            Clients should call recv_tcp instead.
09893 
09894   Input:
09895     INT   index              Index of server connection
09896     DWORD buffer_size        Size of the buffer in bytes.
09897     INT   flags              Flags passed to recv()
09898     INT   convert_flags      Convert flags needed for big/little
09899                              endian conversion
09900 
09901   Output:
09902     char  *buffer            Network receive buffer.
09903     INT   *remaining         Remaining data in cache
09904 
09905   Function value:
09906     INT                      Same as recv()
09907 
09908 \********************************************************************/
09909 {
09910    INT size, param_size;
09911    NET_COMMAND *nc;
09912    INT write_ptr, read_ptr, misalign;
09913    char *net_buffer;
09914    INT copied, status;
09915    INT sock;
09916 
09917    sock = _server_acception[index].recv_sock;
09918 
09919    if (flags & MSG_PEEK) {
09920       status = recv(sock, buffer, buffer_size, flags);
09921       if (status == -1)
09922          cm_msg(MERROR, "recv_tcp_server",
09923                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
09924                 errno, strerror(errno));
09925       return status;
09926    }
09927 
09928    if (!_server_acception[index].net_buffer) {
09929       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
09930          _server_acception[index].net_buffer_size = NET_TCP_SIZE;
09931       else
09932          _server_acception[index].net_buffer_size = NET_BUFFER_SIZE;
09933 
09934       _server_acception[index].net_buffer =
09935           (char *) M_MALLOC(_server_acception[index].net_buffer_size);
09936       _server_acception[index].write_ptr = 0;
09937       _server_acception[index].read_ptr = 0;
09938       _server_acception[index].misalign = 0;
09939    }
09940    if (!_server_acception[index].net_buffer) {
09941       cm_msg(MERROR, "recv_tcp_server", "not enough memory to allocate network buffer");
09942       return -1;
09943    }
09944 
09945    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
09946       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
09947       return -1;
09948    }
09949 
09950    copied = 0;
09951    param_size = -1;
09952 
09953    write_ptr = _server_acception[index].write_ptr;
09954    read_ptr = _server_acception[index].read_ptr;
09955    misalign = _server_acception[index].misalign;
09956    net_buffer = _server_acception[index].net_buffer;
09957 
09958    do {
09959       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
09960          if (param_size == -1) {
09961             if (copied > 0) {
09962                /* assemble split header */
09963                memcpy(buffer + copied, net_buffer + read_ptr,
09964                       (INT) sizeof(NET_COMMAND_HEADER) - copied);
09965                nc = (NET_COMMAND *) (buffer);
09966             } else
09967                nc = (NET_COMMAND *) (net_buffer + read_ptr);
09968 
09969             param_size = (INT) nc->header.param_size;
09970 
09971             if (_server_acception[index].convert_flags)
09972                rpc_convert_single(&param_size, TID_DWORD, 0,
09973                                   _server_acception[index].convert_flags);
09974          }
09975 
09976          /* check if parameters fit in buffer */
09977          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
09978             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
09979             _server_acception[index].read_ptr = _server_acception[index].write_ptr = 0;
09980             return -1;
09981          }
09982 
09983          /* check if we have all parameters in buffer */
09984          if (write_ptr - read_ptr >=
09985              param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
09986             break;
09987       }
09988 
09989       /* not enough data, so copy partially and get new */
09990       size = write_ptr - read_ptr;
09991 
09992       if (size > 0) {
09993          memcpy(buffer + copied, net_buffer + read_ptr, size);
09994          copied += size;
09995          read_ptr = write_ptr;
09996       }
09997 #ifdef OS_UNIX
09998       do {
09999          write_ptr =
10000              recv(sock, net_buffer + misalign,
10001                   _server_acception[index].net_buffer_size - 8, flags);
10002 
10003          /* don't return if an alarm signal was cought */
10004       } while (write_ptr == -1 && errno == EINTR);
10005 #else
10006       write_ptr =
10007           recv(sock, net_buffer + misalign, _server_acception[index].net_buffer_size - 8,
10008                flags);
10009 #endif
10010 
10011       /* abort if connection broken */
10012       if (write_ptr <= 0) {
10013          cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)",
10014                 write_ptr, errno, strerror(errno));
10015 
10016          if (remaining)
10017             *remaining = 0;
10018 
10019          return write_ptr;
10020       }
10021 
10022       read_ptr = misalign;
10023       write_ptr += misalign;
10024 
10025       misalign = write_ptr % 8;
10026    } while (TRUE);
10027 
10028    /* copy rest of parameters */
10029    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
10030    memcpy(buffer + copied, net_buffer + read_ptr, size);
10031    read_ptr += size;
10032 
10033    if (remaining) {
10034       /* don't keep rpc_server_receive in an infinite loop */
10035       if (write_ptr - read_ptr < param_size)
10036          *remaining = 0;
10037       else
10038          *remaining = write_ptr - read_ptr;
10039    }
10040 
10041    _server_acception[index].write_ptr = write_ptr;
10042    _server_acception[index].read_ptr = read_ptr;
10043    _server_acception[index].misalign = misalign;
10044 
10045    return size + copied;
10046 }
10047 
10048 
10049 /********************************************************************/
10050 INT recv_tcp_check(int sock)
10051 /********************************************************************\
10052 
10053   Routine: recv_tcp_check
10054 
10055   Purpose: Check if in TCP receive buffer associated with sock is
10056            some data. Called by ss_suspend.
10057 
10058   Input:
10059     INT   sock               TCP receive socket
10060 
10061   Output:
10062     none
10063 
10064   Function value:
10065     INT   count              Number of bytes remaining in TCP buffer
10066 
10067 \********************************************************************/
10068 {
10069    INT index;
10070 
10071    /* figure out to which connection socket belongs */
10072    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10073       if (_server_acception[index].recv_sock == sock)
10074          break;
10075 
10076    return _server_acception[index].write_ptr - _server_acception[index].read_ptr;
10077 }
10078 
10079 
10080 /********************************************************************/
10081 INT recv_event_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10082                       INT * remaining)
10083 /********************************************************************\
10084 
10085   Routine: recv_event_server
10086 
10087   Purpose: TCP event receive routine with local cache. To speed up
10088            network performance, a 64k buffer is read in at once and
10089            split into several RPC command on successive calls to
10090            recv_event_server. Therefore, the number of recv() calls
10091            is minimized.
10092 
10093            This routine is ment to be called by the server process.
10094            Clients should call recv_tcp instead.
10095 
10096   Input:
10097     INT   index              Index of server connection
10098     DWORD buffer_size        Size of the buffer in bytes.
10099     INT   flags              Flags passed to recv()
10100     INT   convert_flags      Convert flags needed for big/little
10101                              endian conversion
10102 
10103   Output:
10104     char  *buffer            Network receive buffer.
10105     INT   *remaining         Remaining data in cache
10106 
10107   Function value:
10108     INT                      Same as recv()
10109 
10110 \********************************************************************/
10111 {
10112    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10113    EVENT_HEADER *pevent;
10114    INT write_ptr, read_ptr, misalign;
10115    char *net_buffer;
10116    INT copied, status;
10117    INT sock;
10118    RPC_SERVER_ACCEPTION *psa;
10119 
10120    psa = &_server_acception[index];
10121    sock = psa->event_sock;
10122 
10123    if (flags & MSG_PEEK) {
10124       status = recv(sock, buffer, buffer_size, flags);
10125       if (status == -1)
10126          cm_msg(MERROR, "recv_event_server",
10127                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10128                 errno, strerror(errno));
10129       return status;
10130    }
10131 
10132    if (!psa->ev_net_buffer) {
10133       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10134          psa->net_buffer_size = NET_TCP_SIZE;
10135       else
10136          psa->net_buffer_size = NET_BUFFER_SIZE;
10137 
10138       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10139       psa->ev_write_ptr = 0;
10140       psa->ev_read_ptr = 0;
10141       psa->ev_misalign = 0;
10142    }
10143    if (!psa->ev_net_buffer) {
10144       cm_msg(MERROR, "recv_event_server", "not enough memory to allocate network buffer");
10145       return -1;
10146    }
10147 
10148    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10149 
10150    if ((INT) buffer_size < header_size) {
10151       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10152       return -1;
10153    }
10154 
10155    copied = 0;
10156    event_size = -1;
10157 
10158    write_ptr = psa->ev_write_ptr;
10159    read_ptr = psa->ev_read_ptr;
10160    misalign = psa->ev_misalign;
10161    net_buffer = psa->ev_net_buffer;
10162 
10163    do {
10164       if (write_ptr - read_ptr >= header_size - copied) {
10165          if (event_size == -1) {
10166             if (copied > 0) {
10167                /* assemble split header */
10168                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10169                pbh = (INT *) buffer;
10170             } else
10171                pbh = (INT *) (net_buffer + read_ptr);
10172 
10173             pevent = (EVENT_HEADER *) (pbh + 1);
10174 
10175             event_size = pevent->data_size;
10176             if (psa->convert_flags)
10177                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10178 
10179             aligned_event_size = ALIGN8(event_size);
10180          }
10181 
10182          /* check if data part fits in buffer */
10183          if ((INT) buffer_size < aligned_event_size + header_size) {
10184             cm_msg(MERROR, "recv_event_server",
10185                    "parameters too large for network buffer");
10186             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10187             return -1;
10188          }
10189 
10190          /* check if we have whole event in buffer */
10191          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10192             break;
10193       }
10194 
10195       /* not enough data, so copy partially and get new */
10196       size = write_ptr - read_ptr;
10197 
10198       if (size > 0) {
10199          memcpy(buffer + copied, net_buffer + read_ptr, size);
10200          copied += size;
10201          read_ptr = write_ptr;
10202       }
10203 #ifdef OS_UNIX
10204       do {
10205          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10206 
10207          /* don't return if an alarm signal was cought */
10208       } while (write_ptr == -1 && errno == EINTR);
10209 #else
10210       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10211 #endif
10212 
10213       /* abort if connection broken */
10214       if (write_ptr <= 0) {
10215          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)",
10216                 write_ptr, errno, strerror(errno));
10217 
10218          if (remaining)
10219             *remaining = 0;
10220 
10221          return write_ptr;
10222       }
10223 
10224       read_ptr = misalign;
10225       write_ptr += misalign;
10226 
10227       misalign = write_ptr % 8;
10228    } while (TRUE);
10229 
10230    /* copy rest of event */
10231    size = aligned_event_size + header_size - copied;
10232    if (size > 0) {
10233       memcpy(buffer + copied, net_buffer + read_ptr, size);
10234       read_ptr += size;
10235    }
10236 
10237    if (remaining)
10238       *remaining = write_ptr - read_ptr;
10239 
10240    psa->ev_write_ptr = write_ptr;
10241    psa->ev_read_ptr = read_ptr;
10242    psa->ev_misalign = misalign;
10243 
10244    /* convert header little endian/big endian */
10245    if (psa->convert_flags) {
10246       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10247 
10248       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10249       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10250       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10251       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10252       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10253       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10254    }
10255 
10256    return header_size + event_size;
10257 }
10258 
10259 
10260 /********************************************************************/
10261 INT recv_event_check(int sock)
10262 /********************************************************************\
10263 
10264   Routine: recv_event_check
10265 
10266   Purpose: Check if in TCP event receive buffer associated with sock
10267            is some data. Called by ss_suspend.
10268 
10269   Input:
10270     INT   sock               TCP receive socket
10271 
10272   Output:
10273     none
10274 
10275   Function value:
10276     INT   count              Number of bytes remaining in TCP buffer
10277 
10278 \********************************************************************/
10279 {
10280    INT index;
10281 
10282    /* figure out to which connection socket belongs */
10283    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10284       if (_server_acception[index].event_sock == sock)
10285          break;
10286 
10287    return _server_acception[index].ev_write_ptr - _server_acception[index].ev_read_ptr;
10288 }
10289 
10290 
10291 /********************************************************************/
10292 INT rpc_register_server(INT server_type, char *name, INT * port,
10293                         INT(*func) (INT, void **))
10294 /********************************************************************\
10295 
10296   Routine: rpc_register_server
10297 
10298   Purpose: Register the calling process as a MIDAS RPC server. Note
10299            that cm_connnect_experiment must be called prior to any call of
10300            rpc_register_server.
10301 
10302   Input:
10303     INT   server_type       One of the following constants:
10304                             ST_SINGLE: register a single process server
10305                             ST_MTHREAD: for each connection, start
10306                                         a new thread to serve it
10307                             ST_MPROCESS: for each connection, start
10308                                          a new process to server it
10309                             ST_SUBPROCESS: the routine was called from
10310                                            a multi process server
10311                             ST_REMOTE: register a client program server
10312                                        connected to the ODB
10313     char  *name             Name of .EXE file to start in MPROCESS mode
10314     INT   *port             TCP port for listen. NULL if listen as main
10315                             server (MIDAS_TCP_PORT is then used). If *port=0,
10316                             the OS chooses a free port and returns it. If
10317                             *port != 0, this port is used.
10318     INT   *func             Default dispatch function
10319 
10320   Output:
10321     INT   *port             Port under which server is listening.
10322 
10323   Function value:
10324     RPC_SUCCESS             Successful completion
10325     RPC_NET_ERROR           Error in socket call
10326     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10327 
10328 \********************************************************************/
10329 {
10330    struct sockaddr_in bind_addr;
10331    INT status, flag;
10332    unsigned int size;
10333 
10334 #ifdef OS_WINNT
10335    {
10336       WSADATA WSAData;
10337 
10338       /* Start windows sockets */
10339       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10340          return RPC_NET_ERROR;
10341    }
10342 #endif
10343 
10344    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10345 
10346    /* register system functions */
10347    rpc_register_functions(rpc_get_internal_list(0), func);
10348 
10349    if (name != NULL)
10350       rpc_set_server_option(RPC_OSERVER_NAME, (POINTER_T) name);
10351 
10352    /* in subprocess mode, don't start listener */
10353    if (server_type == ST_SUBPROCESS)
10354       return RPC_SUCCESS;
10355 
10356    /* create a socket for listening */
10357    _lsock = socket(AF_INET, SOCK_STREAM, 0);
10358    if (_lsock == -1) {
10359       cm_msg(MERROR, "rpc_register_server", "socket() failed");
10360       return RPC_NET_ERROR;
10361    }
10362 
10363    /* reuse address, needed if previous server stopped (30s timeout!) */
10364    flag = 1;
10365    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
10366    if (status < 0) {
10367       cm_msg(MERROR, "rpc_register_server", "setsockopt() failed");
10368       return RPC_NET_ERROR;
10369    }
10370 
10371    /* bind local node name and port to socket */
10372    memset(&bind_addr, 0, sizeof(bind_addr));
10373    bind_addr.sin_family = AF_INET;
10374    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
10375 
10376    if (!port)
10377       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
10378    else
10379       bind_addr.sin_port = htons((short) (*port));
10380 
10381    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
10382    if (status < 0) {
10383       cm_msg(MERROR, "rpc_register_server", "bind() failed: %s", strerror(errno));
10384       return RPC_NET_ERROR;
10385    }
10386 
10387    /* listen for connection */
10388 #ifdef OS_MSDOS
10389    status = listen(_lsock, 1);
10390 #else
10391    status = listen(_lsock, SOMAXCONN);
10392 #endif
10393    if (status < 0) {
10394       cm_msg(MERROR, "rpc_register_server", "listen() failed");
10395       return RPC_NET_ERROR;
10396    }
10397 
10398    /* return port wich OS has choosen */
10399    if (port && *port == 0) {
10400       size = sizeof(bind_addr);
10401       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *)&size);
10402       *port = ntohs(bind_addr.sin_port);
10403    }
10404 
10405    /* define callbacks for ss_suspend */
10406    if (server_type == ST_REMOTE)
10407       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
10408    else
10409       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
10410 
10411    return RPC_SUCCESS;
10412 }
10413 
10414 
10415 /********************************************************************/
10416 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
10417 /********************************************************************\
10418 
10419   Routine: rpc_execute
10420 
10421   Purpose: Execute a RPC command received over the network
10422 
10423   Input:
10424     INT  sock               TCP socket to which the result should be
10425                             send back
10426 
10427     char *buffer            Command buffer
10428     INT  convert_flags      Flags for data conversion
10429 
10430   Output:
10431     none
10432 
10433   Function value:
10434     RPC_SUCCESS             Successful completion
10435     RPC_INVALID_ID          Invalid routine_id received
10436     RPC_NET_ERROR           Error in socket call
10437     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10438     RPC_SHUTDOWN            Shutdown requested
10439     SS_ABORT                TCP connection broken
10440     SS_EXIT                 TCP connection closed
10441 
10442 \********************************************************************/
10443 {
10444    INT i, index, routine_id, status;
10445    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10446    INT tid, flags;
10447    NET_COMMAND *nc_in, *nc_out;
10448    INT param_size, max_size;
10449    void *prpc_param[20];
10450    char str[1024], debug_line[1024];
10451 
10452 /* return buffer must be auto for multi-thread servers */
10453    char return_buffer[NET_BUFFER_SIZE];
10454 
10455 
10456    /* extract pointer array to parameters */
10457    nc_in = (NET_COMMAND *) buffer;
10458    nc_out = (NET_COMMAND *) return_buffer;
10459 
10460    /* convert header format (byte swapping) */
10461    if (convert_flags) {
10462       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
10463       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
10464    }
10465 
10466    /* no result return in FAST TCP mode */
10467    if (nc_in->header.routine_id & TCP_FAST)
10468       sock = 0;
10469 
10470    /* find entry in rpc_list */
10471    routine_id = nc_in->header.routine_id & ~TCP_FAST;
10472 
10473    for (i = 0;; i++)
10474       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
10475          break;
10476    index = i;
10477    if (rpc_list[i].id == 0) {
10478       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
10479       return RPC_INVALID_ID;
10480    }
10481 
10482    in_param_ptr = nc_in->param;
10483    out_param_ptr = nc_out->param;
10484 
10485    sprintf(debug_line, "%s(", rpc_list[index].name);
10486 
10487    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10488       tid = rpc_list[index].param[i].tid;
10489       flags = rpc_list[index].param[i].flags;
10490 
10491       if (flags & RPC_IN) {
10492          param_size = ALIGN8(tid_size[tid]);
10493 
10494          if (tid == TID_STRING || tid == TID_LINK)
10495             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10496 
10497          if (flags & RPC_VARARRAY) {
10498             /* for arrays, the size is stored as a INT in front of the array */
10499             param_size = *((INT *) in_param_ptr);
10500             if (convert_flags)
10501                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
10502             param_size = ALIGN8(param_size);
10503 
10504             in_param_ptr += ALIGN8(sizeof(INT));
10505          }
10506 
10507          if (tid == TID_STRUCT)
10508             param_size = ALIGN8(rpc_list[index].param[i].n);
10509 
10510          prpc_param[i] = in_param_ptr;
10511 
10512          /* convert data format */
10513          if (convert_flags) {
10514             if (flags & RPC_VARARRAY)
10515                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
10516             else
10517                rpc_convert_data(in_param_ptr, tid, flags,
10518                                 rpc_list[index].param[i].n * tid_size[tid],
10519                                 convert_flags);
10520          }
10521 
10522          db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
10523          if (rpc_list[index].param[i].tid == TID_STRING) {
10524             /* check for long strings (db_create_record...) */
10525             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10526                strcat(debug_line, "\"");
10527                strcat(debug_line, str);
10528                strcat(debug_line, "\"");
10529             } else
10530                strcat(debug_line, "...");
10531          } else
10532             strcat(debug_line, str);
10533 
10534          in_param_ptr += param_size;
10535       }
10536 
10537       if (flags & RPC_OUT) {
10538          param_size = ALIGN8(tid_size[tid]);
10539 
10540          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10541             /* save maximum array length */
10542             max_size = *((INT *) in_param_ptr);
10543             if (convert_flags)
10544                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
10545             max_size = ALIGN8(max_size);
10546 
10547             *((INT *) out_param_ptr) = max_size;
10548 
10549             /* save space for return array length */
10550             out_param_ptr += ALIGN8(sizeof(INT));
10551 
10552             /* use maximum array length from input */
10553             param_size += max_size;
10554          }
10555 
10556          if (rpc_list[index].param[i].tid == TID_STRUCT)
10557             param_size = ALIGN8(rpc_list[index].param[i].n);
10558 
10559          if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size >
10560              NET_BUFFER_SIZE) {
10561             cm_msg(MERROR, "rpc_execute",
10562                    "return parameters (%d) too large for network buffer (%d)",
10563                    (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size,
10564                    NET_BUFFER_SIZE);
10565             return RPC_EXCEED_BUFFER;
10566          }
10567 
10568          /* if parameter goes both directions, copy input to output */
10569          if (rpc_list[index].param[i].flags & RPC_IN)
10570             memcpy(out_param_ptr, prpc_param[i], param_size);
10571 
10572          if (_debug_print && !(flags & RPC_IN))
10573             strcat(debug_line, "-");
10574 
10575          prpc_param[i] = out_param_ptr;
10576          out_param_ptr += param_size;
10577       }
10578 
10579       if (rpc_list[index].param[i + 1].tid)
10580          strcat(debug_line, ", ");
10581    }
10582 
10583    strcat(debug_line, ")");
10584    rpc_debug_printf(debug_line);
10585 
10586    last_param_ptr = out_param_ptr;
10587 
10588   /*********************************\
10589   *   call dispatch function        *
10590   \*********************************/
10591    if (rpc_list[index].dispatch)
10592       status = rpc_list[index].dispatch(routine_id, prpc_param);
10593    else
10594       status = RPC_INVALID_ID;
10595 
10596    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
10597        routine_id == RPC_ID_WATCHDOG)
10598       status = RPC_SUCCESS;
10599 
10600    /* return immediately for closed down client connections */
10601    if (!sock && routine_id == RPC_ID_EXIT)
10602       return SS_EXIT;
10603 
10604    if (!sock && routine_id == RPC_ID_SHUTDOWN)
10605       return RPC_SHUTDOWN;
10606 
10607    /* Return if TCP connection broken */
10608    if (status == SS_ABORT)
10609       return SS_ABORT;
10610 
10611    /* if sock == 0, we are in FTCP mode and may not sent results */
10612    if (!sock)
10613       return RPC_SUCCESS;
10614 
10615    /* compress variable length arrays */
10616    out_param_ptr = nc_out->param;
10617    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10618       if (rpc_list[index].param[i].flags & RPC_OUT) {
10619          tid = rpc_list[index].param[i].tid;
10620          flags = rpc_list[index].param[i].flags;
10621          param_size = ALIGN8(tid_size[tid]);
10622 
10623          if (tid == TID_STRING) {
10624             max_size = *((INT *) out_param_ptr);
10625             param_size = strlen((char *) prpc_param[i]) + 1;
10626             param_size = ALIGN8(param_size);
10627 
10628             /* move string ALIGN8(sizeof(INT)) left */
10629             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
10630 
10631             /* move remaining parameters to end of string */
10632             memcpy(out_param_ptr + param_size,
10633                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
10634                    (POINTER_T) last_param_ptr -
10635                    ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
10636          }
10637 
10638          if (flags & RPC_VARARRAY) {
10639             /* store array length at current out_param_ptr */
10640             max_size = *((INT *) out_param_ptr);
10641             param_size = *((INT *) prpc_param[i + 1]);
10642             *((INT *) out_param_ptr) = param_size;
10643             if (convert_flags)
10644                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
10645 
10646             out_param_ptr += ALIGN8(sizeof(INT));
10647 
10648             param_size = ALIGN8(param_size);
10649 
10650             /* move remaining parameters to end of array */
10651             memcpy(out_param_ptr + param_size,
10652                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
10653                    (POINTER_T) last_param_ptr -
10654                    ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
10655          }
10656 
10657          if (tid == TID_STRUCT)
10658             param_size = ALIGN8(rpc_list[index].param[i].n);
10659 
10660          /* convert data format */
10661          if (convert_flags) {
10662             if (flags & RPC_VARARRAY)
10663                rpc_convert_data(out_param_ptr, tid,
10664                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
10665                                 param_size, convert_flags);
10666             else
10667                rpc_convert_data(out_param_ptr, tid,
10668                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
10669                                 rpc_list[index].param[i].n * tid_size[tid],
10670                                 convert_flags);
10671          }
10672 
10673          out_param_ptr += param_size;
10674       }
10675 
10676    /* send return parameters */
10677    param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
10678    nc_out->header.routine_id = status;
10679    nc_out->header.param_size = param_size;
10680 
10681    /* convert header format (byte swapping) if necessary */
10682    if (convert_flags) {
10683       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD,
10684                          RPC_OUTGOING, convert_flags);
10685       rpc_convert_single(&nc_out->header.param_size, TID_DWORD,
10686                          RPC_OUTGOING, convert_flags);
10687    }
10688 
10689    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
10690 
10691    if (status < 0) {
10692       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
10693       return RPC_NET_ERROR;
10694    }
10695 
10696    /* print return buffer */
10697 /*
10698   printf("Return buffer, ID %d:\n", routine_id);
10699   for (i=0; i<param_size ; i++)
10700     {
10701     status = (char) nc_out->param[i];
10702     printf("%02X ", status);
10703     if (i%8 == 7)
10704       printf("\n");
10705     }
10706 */
10707    /* return SS_EXIT if RPC_EXIT is called */
10708    if (routine_id == RPC_ID_EXIT)
10709       return SS_EXIT;
10710 
10711    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
10712    if (routine_id == RPC_ID_SHUTDOWN)
10713       return RPC_SHUTDOWN;
10714 
10715    return RPC_SUCCESS;
10716 }
10717 
10718 
10719 /********************************************************************/
10720 INT rpc_execute_ascii(INT sock, char *buffer)
10721 /********************************************************************\
10722 
10723   Routine: rpc_execute_ascii
10724 
10725   Purpose: Execute a RPC command received over the network in ASCII
10726            mode
10727 
10728   Input:
10729     INT  sock               TCP socket to which the result should be
10730                             send back
10731 
10732     char *buffer            Command buffer
10733 
10734   Output:
10735     none
10736 
10737   Function value:
10738     RPC_SUCCESS             Successful completion
10739     RPC_INVALID_ID          Invalid routine_id received
10740     RPC_NET_ERROR           Error in socket call
10741     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10742     RPC_SHUTDOWN            Shutdown requested
10743     SS_ABORT                TCP connection broken
10744     SS_EXIT                 TCP connection closed
10745 
10746 \********************************************************************/
10747 {
10748 #define ASCII_BUFFER_SIZE 64500
10749 #define N_APARAM           1024
10750 
10751    INT i, j, index, status, index_in;
10752    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10753    INT routine_id, tid, flags, array_tid, n_param;
10754    INT param_size, item_size, num_values;
10755    void *prpc_param[20];
10756    char *arpc_param[N_APARAM], *pc;
10757    char str[1024], debug_line[1024];
10758    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
10759    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
10760    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
10761 
10762    /* parse arguments */
10763    arpc_param[0] = buffer;
10764    for (i = 1; i < N_APARAM; i++) {
10765       arpc_param[i] = strchr(arpc_param[i - 1], '&');
10766       if (arpc_param[i] == NULL)
10767          break;
10768       *arpc_param[i] = 0;
10769       arpc_param[i]++;
10770    }
10771 
10772    /* decode '%' */
10773    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
10774       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
10775          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
10776             str[0] = pc[1];
10777             str[1] = pc[2];
10778             str[2] = 0;
10779             sscanf(str, "%02X", &i);
10780 
10781             *pc++ = i;
10782             while (pc[2]) {
10783                pc[0] = pc[2];
10784                pc++;
10785             }
10786          }
10787       }
10788 
10789    /* find entry in rpc_list */
10790    for (i = 0;; i++)
10791       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
10792          break;
10793    index = i;
10794    routine_id = rpc_list[i].id;
10795    if (rpc_list[i].id == 0) {
10796       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
10797       return RPC_INVALID_ID;
10798    }
10799 
10800    in_param_ptr = buffer1;
10801    out_param_ptr = buffer2;
10802    index_in = 1;
10803 
10804    sprintf(debug_line, "%s(", rpc_list[index].name);
10805 
10806    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10807       tid = rpc_list[index].param[i].tid;
10808       flags = rpc_list[index].param[i].flags;
10809 
10810       if (flags & RPC_IN) {
10811          if (flags & RPC_VARARRAY) {
10812             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
10813 
10814             prpc_param[i] = in_param_ptr;
10815             for (j = 0; j < n_param; j++) {
10816                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
10817                in_param_ptr += param_size;
10818             }
10819             in_param_ptr = (char *) ALIGN8(((POINTER_T) in_param_ptr));
10820 
10821             strcat(debug_line, "<array>");
10822          } else {
10823             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
10824             param_size = ALIGN8(param_size);
10825 
10826             if (tid == TID_STRING || tid == TID_LINK)
10827                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10828 
10829             /*
10830                if (tid == TID_STRUCT)
10831                param_size = ALIGN8( rpc_list[index].param[i].n );
10832              */
10833             prpc_param[i] = in_param_ptr;
10834 
10835             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
10836             if (rpc_list[index].param[i].tid == TID_STRING) {
10837                /* check for long strings (db_create_record...) */
10838                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10839                   strcat(debug_line, "\"");
10840                   strcat(debug_line, str);
10841                   strcat(debug_line, "\"");
10842                } else
10843                   strcat(debug_line, "...");
10844             } else
10845                strcat(debug_line, str);
10846 
10847             in_param_ptr += param_size;
10848          }
10849 
10850          if ((POINTER_T) in_param_ptr - (POINTER_T) buffer1 > ASCII_BUFFER_SIZE) {
10851             cm_msg(MERROR, "rpc_ascii_execute",
10852                    "parameters (%d) too large for network buffer (%d)", param_size,
10853                    ASCII_BUFFER_SIZE);
10854             return RPC_EXCEED_BUFFER;
10855          }
10856 
10857       }
10858 
10859       if (flags & RPC_OUT) {
10860          param_size = ALIGN8(tid_size[tid]);
10861 
10862          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10863             /* reserve maximum array length */
10864             param_size = atoi(arpc_param[index_in]);
10865             param_size = ALIGN8(param_size);
10866          }
10867 
10868 /*
10869       if (rpc_list[index].param[i].tid == TID_STRUCT)
10870         param_size = ALIGN8( rpc_list[index].param[i].n );
10871 */
10872          if ((POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size >
10873              ASCII_BUFFER_SIZE) {
10874             cm_msg(MERROR, "rpc_execute",
10875                    "return parameters (%d) too large for network buffer (%d)",
10876                    (POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size,
10877                    ASCII_BUFFER_SIZE);
10878             return RPC_EXCEED_BUFFER;
10879          }
10880 
10881          /* if parameter goes both directions, copy input to output */
10882          if (rpc_list[index].param[i].flags & RPC_IN)
10883             memcpy(out_param_ptr, prpc_param[i], param_size);
10884 
10885          if (!(flags & RPC_IN))
10886             strcat(debug_line, "-");
10887 
10888          prpc_param[i] = out_param_ptr;
10889          out_param_ptr += param_size;
10890       }
10891 
10892       if (rpc_list[index].param[i + 1].tid)
10893          strcat(debug_line, ", ");
10894    }
10895 
10896    strcat(debug_line, ")");
10897    rpc_debug_printf(debug_line);
10898 
10899    last_param_ptr = out_param_ptr;
10900 
10901    /*********************************\
10902    *   call dispatch function        *
10903    \*********************************/
10904 
10905    if (rpc_list[index].dispatch)
10906       status = rpc_list[index].dispatch(routine_id, prpc_param);
10907    else
10908       status = RPC_INVALID_ID;
10909 
10910    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
10911        routine_id == RPC_ID_WATCHDOG)
10912       status = RPC_SUCCESS;
10913 
10914    /* Return if TCP connection broken */
10915    if (status == SS_ABORT)
10916       return SS_ABORT;
10917 
10918    /* if sock == 0, we are in FTCP mode and may not sent results */
10919    if (!sock)
10920       return RPC_SUCCESS;
10921 
10922    /* send return status */
10923    out_param_ptr = return_buffer;
10924    sprintf(out_param_ptr, "%d", status);
10925    out_param_ptr += strlen(out_param_ptr);
10926 
10927    /* convert return parameters */
10928    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10929       if (rpc_list[index].param[i].flags & RPC_OUT) {
10930          *out_param_ptr++ = '&';
10931 
10932          tid = rpc_list[index].param[i].tid;
10933          flags = rpc_list[index].param[i].flags;
10934          param_size = ALIGN8(tid_size[tid]);
10935 
10936          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
10937             strcpy(out_param_ptr, (char *) prpc_param[i]);
10938             param_size = strlen((char *) prpc_param[i]);
10939          }
10940 
10941          else if (flags & RPC_VARARRAY) {
10942             if (rpc_list[index].id == RPC_BM_RECEIVE_EVENT) {
10943                param_size = *((INT *) prpc_param[i + 1]);
10944                /* write number of bytes to output */
10945                sprintf(out_param_ptr, "%d", param_size);
10946                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
10947                memcpy(out_param_ptr, prpc_param[i], param_size);
10948                out_param_ptr += param_size;
10949                *out_param_ptr = 0;
10950             } else {
10951                if (rpc_list[index].id == RPC_DB_GET_DATA1) {
10952                   param_size = *((INT *) prpc_param[i + 1]);
10953                   array_tid = *((INT *) prpc_param[i + 2]);
10954                   num_values = *((INT *) prpc_param[i + 3]);
10955                } else if (rpc_list[index].id == RPC_DB_GET_DATA_INDEX) {
10956                   param_size = *((INT *) prpc_param[i + 1]);
10957                   array_tid = *((INT *) prpc_param[i + 3]);
10958                   num_values = 1;
10959                } else if (rpc_list[index].id == RPC_HS_READ) {
10960                   param_size = *((INT *) prpc_param[i + 1]);
10961                   if (i == 6) {
10962                      array_tid = TID_DWORD;
10963                      num_values = param_size / sizeof(DWORD);
10964                   } else {
10965                      array_tid = *((INT *) prpc_param[10]);
10966                      num_values = *((INT *) prpc_param[11]);
10967                   }
10968                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
10969 
10970                   param_size = *((INT *) prpc_param[i + 1]);
10971                   array_tid = tid;
10972                   if (tid == TID_STRING)
10973                      num_values = param_size / NAME_LENGTH;
10974                   else
10975                      num_values = param_size / tid_size[tid];
10976                }
10977 
10978                /* derive size of individual item */
10979                if (array_tid == TID_STRING)
10980                   item_size = param_size / num_values;
10981                else
10982                   item_size = tid_size[array_tid];
10983 
10984                /* write number of elements to output */
10985                sprintf(out_param_ptr, "%d", num_values);
10986                out_param_ptr += strlen(out_param_ptr);
10987 
10988                /* write array of values to output */
10989                for (j = 0; j < num_values; j++) {
10990                   *out_param_ptr++ = '&';
10991                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
10992                   out_param_ptr += strlen(out_param_ptr);
10993                }
10994             }
10995          }
10996 
10997 /*
10998       else if (tid == TID_STRUCT)
10999         param_size = ALIGN8( rpc_list[index].param[i].n );
11000 */
11001          else
11002             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
11003 
11004          out_param_ptr += strlen(out_param_ptr);
11005 
11006          if ((POINTER_T) out_param_ptr - (POINTER_T) return_buffer > ASCII_BUFFER_SIZE) {
11007             cm_msg(MERROR, "rpc_execute",
11008                    "return parameter (%d) too large for network buffer (%d)", param_size,
11009                    ASCII_BUFFER_SIZE);
11010             return RPC_EXCEED_BUFFER;
11011          }
11012       }
11013 
11014    /* send return parameters */
11015    param_size = (POINTER_T) out_param_ptr - (POINTER_T) return_buffer + 1;
11016 
11017    status = send_tcp(sock, return_buffer, param_size, 0);
11018 
11019    if (status < 0) {
11020       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11021       return RPC_NET_ERROR;
11022    }
11023 
11024    /* print return buffer */
11025    if (strlen(return_buffer) > sizeof(debug_line)) {
11026       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
11027       strcat(debug_line, "...");
11028    } else
11029       sprintf(debug_line, "-> %s", return_buffer);
11030    rpc_debug_printf(debug_line);
11031 
11032    /* return SS_EXIT if RPC_EXIT is called */
11033    if (routine_id == RPC_ID_EXIT)
11034       return SS_EXIT;
11035 
11036    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11037    if (routine_id == RPC_ID_SHUTDOWN)
11038       return RPC_SHUTDOWN;
11039 
11040    return RPC_SUCCESS;
11041 }
11042 
11043 
11044 /********************************************************************/
11045 INT rpc_server_accept(int lsock)
11046 /********************************************************************\
11047 
11048   Routine: rpc_server_accept
11049 
11050   Purpose: Accept new incoming connections
11051 
11052   Input:
11053     INT    lscok            Listen socket
11054 
11055   Output:
11056     none
11057 
11058   Function value:
11059     RPC_SUCCESS             Successful completion
11060     RPC_NET_ERROR           Error in socket call
11061     RPC_CONNCLOSED          Connection was closed
11062     RPC_SHUTDOWN            Listener shutdown
11063     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11064 
11065 \********************************************************************/
11066 {
11067    INT index, i;
11068    unsigned int size;
11069    int status;
11070    char command, version[32], v1[32];
11071    INT sock, port1, port2, port3;
11072    struct sockaddr_in acc_addr;
11073    struct hostent *phe;
11074    char str[100];
11075    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11076    char debug_str[30];
11077    char *argv[10];
11078    char net_buffer[256];
11079    struct linger ling;
11080 
11081    static struct callback_addr callback;
11082 
11083    if (lsock > 0) {
11084       size = sizeof(acc_addr);
11085       sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *)&size);
11086 
11087       if (sock == -1)
11088          return RPC_NET_ERROR;
11089    } else {
11090       /* lsock is stdin -> already connected from inetd */
11091 
11092       size = sizeof(acc_addr);
11093       sock = lsock;
11094       getpeername(sock, (struct sockaddr *) &acc_addr, (int *)&size);
11095    }
11096 
11097    /* receive string with timeout */
11098    i = recv_string(sock, net_buffer, 256, 10000);
11099    rpc_debug_printf("Received command: %s", net_buffer);
11100 
11101    if (i > 0) {
11102       command = (char) toupper(net_buffer[0]);
11103 
11104       switch (command) {
11105       case 'S':
11106 
11107          /*----------- shutdown listener ----------------------*/
11108          closesocket(sock);
11109          return RPC_SHUTDOWN;
11110 
11111       case 7:
11112          ss_shell(sock);
11113          closesocket(sock);
11114          break;
11115 
11116       case 'I':
11117 
11118          /*----------- return available experiments -----------*/
11119          cm_scan_experiments();
11120          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11121             sprintf(str, "%s", exptab[i].name);
11122             send(sock, str, strlen(str) + 1, 0);
11123          }
11124          send(sock, "", 1, 0);
11125          closesocket(sock);
11126          break;
11127 
11128       case 'C':
11129 
11130          /*----------- connect to experiment -----------*/
11131 
11132          /* get callback information */
11133          callback.experiment[0] = 0;
11134          port1 = port2 = version[0] = 0;
11135          sscanf(net_buffer + 2, "%d %d %d %s", &port1, &port2, &port3, version);
11136          strcpy(callback.experiment,
11137                 strchr(strchr(strchr(strchr(net_buffer + 2, ' ') + 1, ' ') + 1, ' ') + 1,
11138                        ' ') + 1);
11139 
11140          /* print warning if version patch level doesn't agree */
11141          strcpy(v1, version);
11142          if (strchr(v1, '.'))
11143             if (strchr(strchr(v1, '.') + 1, '.'))
11144                *strchr(strchr(v1, '.') + 1, '.') = 0;
11145 
11146          strcpy(str, cm_get_version());
11147          if (strchr(str, '.'))
11148             if (strchr(strchr(str, '.') + 1, '.'))
11149                *strchr(strchr(str, '.') + 1, '.') = 0;
11150 
11151          if (strcmp(v1, str) != 0) {
11152             sprintf(str, "client MIDAS version %s differs from local version %s",
11153                     version, cm_get_version());
11154             cm_msg(MERROR, "rpc_server_accept", str);
11155 
11156             sprintf(str, "received string: %s", net_buffer + 2);
11157             cm_msg(MERROR, "rpc_server_accept", str);
11158          }
11159 
11160          callback.host_port1 = (short) port1;
11161          callback.host_port2 = (short) port2;
11162          callback.host_port3 = (short) port3;
11163          callback.debug = _debug_mode;
11164 
11165          /* get the name of the remote host */
11166 #ifdef OS_VXWORKS
11167          {
11168             INT status;
11169             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11170             if (status != 0) {
11171                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11172                break;
11173             }
11174          }
11175 #else
11176          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11177          if (phe == NULL) {
11178             /* use IP number instead */
11179             strcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr));
11180          } else
11181             strcpy(callback.host_name, phe->h_name);
11182 #endif
11183 
11184          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11185             /* update experiment definition */
11186             cm_scan_experiments();
11187 
11188             /* lookup experiment */
11189             if (equal_ustring(callback.experiment, "Default"))
11190                index = 0;
11191             else
11192                for (index = 0; index < MAX_EXPERIMENT && exptab[index].name[0]; index++)
11193                   if (equal_ustring(callback.experiment, exptab[index].name))
11194                      break;
11195 
11196             if (index == MAX_EXPERIMENT || exptab[index].name[0] == 0) {
11197                sprintf(str, "experiment %s not defined in exptab\r", callback.experiment);
11198                cm_msg(MERROR, "rpc_server_accept", str);
11199 
11200                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11201                closesocket(sock);
11202                break;
11203             }
11204 
11205             strcpy(callback.directory, exptab[index].directory);
11206             strcpy(callback.user, exptab[index].user);
11207 
11208             /* create a new process */
11209             sprintf(host_port1_str, "%d", callback.host_port1);
11210             sprintf(host_port2_str, "%d", callback.host_port2);
11211             sprintf(host_port3_str, "%d", callback.host_port3);
11212             sprintf(debug_str, "%d", callback.debug);
11213 
11214             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11215             argv[1] = callback.host_name;
11216             argv[2] = host_port1_str;
11217             argv[3] = host_port2_str;
11218             argv[4] = host_port3_str;
11219             argv[5] = debug_str;
11220             argv[6] = callback.experiment;
11221             argv[7] = callback.directory;
11222             argv[8] = callback.user;
11223             argv[9] = NULL;
11224 
11225             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
11226                              argv[0], argv[1], argv[2], argv[3], argv[4],
11227                              argv[5], argv[6], argv[7], argv[8], argv[9]);
11228 
11229             status = ss_spawnv(P_NOWAIT,
11230                                (char *) rpc_get_server_option(RPC_OSERVER_NAME), argv);
11231 
11232             if (status != SS_SUCCESS) {
11233                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
11234 
11235                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
11236                send(sock, str, strlen(str) + 1, 0);
11237                closesocket(sock);
11238                break;
11239             }
11240 
11241             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11242             send(sock, str, strlen(str) + 1, 0);
11243             closesocket(sock);
11244          } else {
11245             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11246             send(sock, str, strlen(str) + 1, 0);
11247             closesocket(sock);
11248          }
11249 
11250          /* look for next free entry */
11251          for (index = 0; index < MAX_RPC_CONNECTION; index++)
11252             if (_server_acception[index].recv_sock == 0)
11253                break;
11254          if (index == MAX_RPC_CONNECTION)
11255             return RPC_NET_ERROR;
11256          callback.index = index;
11257 
11258         /*----- multi thread server ------------------------*/
11259          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11260             ss_thread_create(rpc_server_thread, (void *) (&callback));
11261 
11262         /*----- single thread server -----------------------*/
11263          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
11264              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11265             rpc_server_callback(&callback);
11266 
11267          break;
11268 
11269       default:
11270          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c'", command);
11271          closesocket(sock);
11272          break;
11273 
11274       }
11275    } else {                     /* if i>0 */
11276 
11277       /* lingering needed for PCTCP */
11278       ling.l_onoff = 1;
11279       ling.l_linger = 0;
11280       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11281       closesocket(sock);
11282    }
11283 
11284    return RPC_SUCCESS;
11285 }
11286 
11287 
11288 /********************************************************************/
11289 INT rpc_client_accept(int lsock)
11290 /********************************************************************\
11291 
11292   Routine: rpc_client_accept
11293 
11294   Purpose: Accept new incoming connections as a client
11295 
11296   Input:
11297     INT    lsock            Listen socket
11298 
11299   Output:
11300     none
11301 
11302   Function value:
11303     RPC_SUCCESS             Successful completion
11304     RPC_NET_ERROR           Error in socket call
11305     RPC_CONNCLOSED          Connection was closed
11306     RPC_SHUTDOWN            Listener shutdown
11307     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11308 
11309 \********************************************************************/
11310 {
11311    INT index, i, version, status;
11312    unsigned int size;
11313    int sock;
11314    struct sockaddr_in acc_addr;
11315    INT client_hw_type = 0, hw_type;
11316    char str[100], client_program[NAME_LENGTH];
11317    char host_name[HOST_NAME_LENGTH];
11318    INT convert_flags;
11319    char net_buffer[256], *p;
11320 
11321    size = sizeof(acc_addr);
11322    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *)&size);
11323 
11324    if (sock == -1)
11325       return RPC_NET_ERROR;
11326 
11327    /* get the name of the calling host */
11328 /* outcommented for speed reasons SR 7.10.98
11329 #ifdef OS_VXWORKS
11330   {
11331   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
11332   if (status != 0)
11333     strcpy(host_name, "unknown");
11334   }
11335 #else
11336   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11337   if (phe == NULL)
11338     strcpy(host_name, "unknown");
11339   strcpy(host_name, phe->h_name);
11340 #endif
11341 */
11342    strcpy(host_name, "");
11343 
11344    /* look for next free entry */
11345    for (index = 0; index < MAX_RPC_CONNECTION; index++)
11346       if (_server_acception[index].recv_sock == 0)
11347          break;
11348    if (index == MAX_RPC_CONNECTION) {
11349       closesocket(sock);
11350       return RPC_NET_ERROR;
11351    }
11352 
11353    /* receive string with timeout */
11354    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
11355    if (i <= 0) {
11356       closesocket(sock);
11357       return RPC_NET_ERROR;
11358    }
11359 
11360    /* get remote computer info */
11361    p = strtok(net_buffer, " ");
11362    if (p != NULL) {
11363       client_hw_type = atoi(p);
11364       p = strtok(NULL, " ");
11365    }
11366    if (p != NULL) {
11367       version = atoi(p);
11368       p = strtok(NULL, " ");
11369    }
11370    if (p != NULL) {
11371       strcpy(client_program, p);
11372       p = strtok(NULL, " ");
11373    }
11374    if (p != NULL) {
11375       strcpy(host_name, p);
11376       p = strtok(NULL, " ");
11377    }
11378 
11379    /* save information in _server_acception structure */
11380    _server_acception[index].recv_sock = sock;
11381    _server_acception[index].send_sock = 0;
11382    _server_acception[index].event_sock = 0;
11383    _server_acception[index].remote_hw_type = client_hw_type;
11384    strcpy(_server_acception[index].host_name, host_name);
11385    strcpy(_server_acception[index].prog_name, client_program);
11386    _server_acception[index].tid = ss_gettid();
11387    _server_acception[index].last_activity = ss_millitime();
11388    _server_acception[index].watchdog_timeout = 0;
11389 
11390    /* send my own computer id */
11391    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11392    sprintf(str, "%d %s", hw_type, cm_get_version());
11393    status = send(sock, str, strlen(str) + 1, 0);
11394    if (status != (INT) strlen(str) + 1)
11395       return RPC_NET_ERROR;
11396 
11397    rpc_set_server_acception(index + 1);
11398    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11399    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11400 
11401    /* set callback function for ss_suspend */
11402    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11403                            (int (*)(void)) rpc_server_receive);
11404 
11405    return RPC_SUCCESS;
11406 }
11407 
11408 
11409 /********************************************************************/
11410 INT rpc_server_callback(struct callback_addr * pcallback)
11411 /********************************************************************\
11412 
11413   Routine: rpc_server_callback
11414 
11415   Purpose: Callback a remote client. Setup _server_acception entry
11416            with optional conversion flags and establish two-way
11417            TCP connection.
11418 
11419   Input:
11420     callback_addr pcallback Pointer to a callback structure
11421 
11422   Output:
11423     none
11424 
11425   Function value:
11426     RPC_SUCCESS             Successful completion
11427 
11428 \********************************************************************/
11429 {
11430    INT index, status;
11431    int recv_sock, send_sock, event_sock;
11432    struct sockaddr_in bind_addr;
11433    struct hostent *phe;
11434    char str[100], client_program[NAME_LENGTH];
11435    char host_name[HOST_NAME_LENGTH];
11436    INT client_hw_type, hw_type;
11437    INT convert_flags;
11438    char net_buffer[256];
11439    struct callback_addr callback;
11440    int flag;
11441 
11442    /* copy callback information */
11443    memcpy(&callback, pcallback, sizeof(callback));
11444    index = callback.index;
11445 
11446    /* create new sockets for TCP */
11447    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
11448    send_sock = socket(AF_INET, SOCK_STREAM, 0);
11449    event_sock = socket(AF_INET, SOCK_STREAM, 0);
11450    if (event_sock == -1)
11451       return RPC_NET_ERROR;
11452 
11453    /* callback to remote node */
11454    memset(&bind_addr, 0, sizeof(bind_addr));
11455    bind_addr.sin_family = AF_INET;
11456    bind_addr.sin_port = htons(callback.host_port1);
11457 
11458 #ifdef OS_VXWORKS
11459    {
11460       INT host_addr;
11461 
11462       host_addr = hostGetByName(callback.host_name);
11463       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
11464    }
11465 #else
11466    phe = gethostbyname(callback.host_name);
11467    if (phe == NULL) {
11468       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
11469       return RPC_NET_ERROR;
11470    }
11471    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
11472 #endif
11473 
11474    /* connect receive socket */
11475 #ifdef OS_UNIX
11476    do {
11477       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
11478 
11479       /* don't return if an alarm signal was cought */
11480    } while (status == -1 && errno == EINTR);
11481 #else
11482    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11483 #endif
11484 
11485    if (status != 0) {
11486       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
11487       goto error;
11488    }
11489 
11490    bind_addr.sin_port = htons(callback.host_port2);
11491 
11492    /* connect send socket */
11493 #ifdef OS_UNIX
11494    do {
11495       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11496 
11497       /* don't return if an alarm signal was cought */
11498    } while (status == -1 && errno == EINTR);
11499 #else
11500    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11501 #endif
11502 
11503    if (status != 0) {
11504       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
11505       goto error;
11506    }
11507 
11508    bind_addr.sin_port = htons(callback.host_port3);
11509 
11510    /* connect event socket */
11511 #ifdef OS_UNIX
11512    do {
11513       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11514 
11515       /* don't return if an alarm signal was cought */
11516    } while (status == -1 && errno == EINTR);
11517 #else
11518    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11519 #endif
11520 
11521    if (status != 0) {
11522       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
11523       goto error;
11524    }
11525 
11526    /* increase receive buffer size to 64k */
11527 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
11528    flag = 0x10000;
11529    setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
11530 #endif
11531 
11532    if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
11533       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
11534       goto error;
11535    }
11536 
11537    /* get remote computer info */
11538    sscanf(net_buffer, "%d", &client_hw_type);
11539 
11540    strcpy(client_program, strchr(net_buffer, ' ') + 1);
11541 
11542    /* get the name of the remote host */
11543 #ifdef OS_VXWORKS
11544    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
11545    if (status != 0)
11546       strcpy(host_name, "unknown");
11547 #else
11548    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
11549    if (phe == NULL)
11550       strcpy(host_name, "unknown");
11551    else
11552       strcpy(host_name, phe->h_name);
11553 #endif
11554 
11555    /* save information in _server_acception structure */
11556    _server_acception[index].recv_sock = recv_sock;
11557    _server_acception[index].send_sock = send_sock;
11558    _server_acception[index].event_sock = event_sock;
11559    _server_acception[index].remote_hw_type = client_hw_type;
11560    strcpy(_server_acception[index].host_name, host_name);
11561    strcpy(_server_acception[index].prog_name, client_program);
11562    _server_acception[index].tid = ss_gettid();
11563    _server_acception[index].last_activity = ss_millitime();
11564    _server_acception[index].watchdog_timeout = 0;
11565 
11566    /* send my own computer id */
11567    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11568    sprintf(str, "%d", hw_type);
11569    send(recv_sock, str, strlen(str) + 1, 0);
11570 
11571    rpc_set_server_acception(index + 1);
11572    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11573    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11574 
11575    /* set callback function for ss_suspend */
11576    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11577                            (int (*)(void)) rpc_server_receive);
11578 
11579    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11580       rpc_debug_printf("Connection to %s:%s established\n",
11581                        _server_acception[index].host_name,
11582                        _server_acception[index].prog_name);
11583 
11584    return RPC_SUCCESS;
11585 
11586  error:
11587 
11588    closesocket(recv_sock);
11589    closesocket(send_sock);
11590    closesocket(event_sock);
11591 
11592    return RPC_NET_ERROR;
11593 }
11594 
11595 
11596 /********************************************************************/
11597 INT rpc_server_thread(void *pointer)
11598 /********************************************************************\
11599 
11600   Routine: rpc_server_thread
11601 
11602   Purpose: New thread for a multi-threaded server. Callback to the
11603            client and process RPC requests.
11604 
11605   Input:
11606     vcoid  pointer          pointer to callback_addr structure.
11607 
11608   Output:
11609     none
11610 
11611   Function value:
11612     RPC_SUCCESS             Successful completion
11613 
11614 \********************************************************************/
11615 {
11616    struct callback_addr callback;
11617    int status, mutex_alarm, mutex_elog;
11618    static DWORD last_checked = 0;
11619 
11620    memcpy(&callback, pointer, sizeof(callback));
11621 
11622    status = rpc_server_callback(&callback);
11623 
11624    if (status != RPC_SUCCESS)
11625       return status;
11626 
11627    /* create alarm and elog mutexes */
11628    ss_mutex_create("ALARM", &mutex_alarm);
11629    ss_mutex_create("ELOG", &mutex_elog);
11630    cm_set_experiment_mutex(mutex_alarm, mutex_elog);
11631 
11632    do {
11633       status = ss_suspend(5000, 0);
11634 
11635       if (rpc_check_channels() == RPC_NET_ERROR)
11636          break;
11637 
11638       /* check alarms every 10 seconds */
11639       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
11640          al_check();
11641          last_checked = ss_time();
11642       }
11643 
11644    } while (status != SS_ABORT && status != SS_EXIT);
11645 
11646    /* delete entry in suspend table for this thread */
11647    ss_suspend_exit();
11648 
11649    return RPC_SUCCESS;
11650 }
11651 
11652 
11653 /********************************************************************/
11654 INT rpc_server_receive(INT index, int sock, BOOL check)
11655 /********************************************************************\
11656 
11657   Routine: rpc_server_receive
11658 
11659   Purpose: Receive rpc commands and execute them. Close the connection
11660            if client has broken TCP pipe.
11661 
11662   Input:
11663     INT    index            Index to _server_acception structure in-
11664                             dicating which connection got data.
11665     int    sock             Socket which got data
11666     BOOL   check            If TRUE, only check if connection is
11667                             broken. This may be called via
11668                             bm_receive_event/ss_suspend(..,MSG_BM)
11669 
11670   Output:
11671     none
11672 
11673   Function value:
11674     RPC_SUCCESS             Successful completion
11675     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
11676     SS_EXIT                 Server connection was closed
11677     SS_ABORT                Server connection was broken
11678 
11679 \********************************************************************/
11680 {
11681    INT status, n_received;
11682    INT remaining, *pbh, start_time;
11683    char test_buffer[256], str[80];
11684    EVENT_HEADER *pevent;
11685 
11686    /* init network buffer */
11687    if (_net_recv_buffer_size == 0) {
11688       _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
11689       if (_net_recv_buffer == NULL) {
11690          cm_msg(MERROR, "rpc_server_receive",
11691                 "not enough memory to allocate network buffer");
11692          return RPC_EXCEED_BUFFER;
11693       }
11694       _net_recv_buffer_size = NET_BUFFER_SIZE;
11695    }
11696 
11697    /* only check if TCP connection is broken */
11698    if (check) {
11699       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
11700 
11701       if (n_received == -1)
11702          cm_msg(MERROR, "rpc_server_receive",
11703                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
11704                 n_received, errno, strerror(errno));
11705 
11706       if (n_received <= 0)
11707          return SS_ABORT;
11708 
11709       return SS_SUCCESS;
11710    }
11711 
11712    remaining = 0;
11713 
11714    /* receive command */
11715    if (sock == _server_acception[index].recv_sock) {
11716       do {
11717          if (_server_acception[index].remote_hw_type == DR_ASCII)
11718             n_received = recv_string(_server_acception[index].recv_sock, _net_recv_buffer,
11719                                      NET_BUFFER_SIZE, 10000);
11720          else
11721             n_received =
11722                 recv_tcp_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
11723 
11724          if (n_received <= 0) {
11725             status = SS_ABORT;
11726             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort",
11727                    n_received);
11728             goto error;
11729          }
11730 
11731          rpc_set_server_acception(index + 1);
11732 
11733          if (_server_acception[index].remote_hw_type == DR_ASCII)
11734             status = rpc_execute_ascii(_server_acception[index].recv_sock,
11735                                        _net_recv_buffer);
11736          else
11737             status = rpc_execute(_server_acception[index].recv_sock,
11738                                  _net_recv_buffer,
11739                                  _server_acception[index].convert_flags);
11740 
11741          if (status == SS_ABORT) {
11742             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort",
11743                    status);
11744             goto error;
11745          }
11746 
11747          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
11748             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11749                rpc_debug_printf("Connection to %s:%s closed\n",
11750                                 _server_acception[index].host_name,
11751                                 _server_acception[index].prog_name);
11752             goto exit;
11753          }
11754 
11755       } while (remaining);
11756    } else {
11757       /* receive event */
11758       if (sock == _server_acception[index].event_sock) {
11759          start_time = ss_millitime();
11760 
11761          do {
11762             n_received =
11763                 recv_event_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0,
11764                                   &remaining);
11765 
11766             if (n_received <= 0) {
11767                status = SS_ABORT;
11768                cm_msg(MERROR, "rpc_server_receive",
11769                       "recv_event_server() returned %d, abort", n_received);
11770                goto error;
11771             }
11772 
11773             /* send event to buffer */
11774             pbh = (INT *) _net_recv_buffer;
11775             pevent = (EVENT_HEADER *) (pbh + 1);
11776 
11777             status =
11778                 bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER),
11779                               SYNC);
11780             if (status != BM_SUCCESS)
11781                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d",
11782                       status);
11783 
11784             /* repeat for maximum 0.5 sec */
11785          } while (ss_millitime() - start_time < 500 && remaining);
11786       }
11787    }
11788 
11789    return RPC_SUCCESS;
11790 
11791  error:
11792 
11793    strcpy(str, _server_acception[index].host_name);
11794    if (strchr(str, '.'))
11795       *strchr(str, '.') = 0;
11796    cm_msg(MTALK, "rpc_server_receive", "Program %s on host %s aborted",
11797           _server_acception[index].prog_name, str);
11798 
11799  exit:
11800 
11801    /* disconnect from experiment as MIDAS server */
11802    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
11803       HNDLE hDB, hKey;
11804 
11805       cm_get_experiment_database(&hDB, &hKey);
11806 
11807       /* only disconnect from experiment if previously connected.
11808          Necessary for pure RPC servers (RPC_SRVR) */
11809       if (hDB) {
11810 #ifdef LOCAL_ROUTINES
11811          ss_alarm(0, cm_watchdog);
11812 #endif
11813 
11814          cm_delete_client_info(hDB, 0);
11815 
11816          bm_close_all_buffers();
11817          db_close_all_databases();
11818 
11819          rpc_deregister_functions();
11820 
11821          cm_set_experiment_database(0, 0);
11822 
11823          _msg_buffer = 0;
11824       }
11825    }
11826 
11827    /* close server connection */
11828    if (_server_acception[index].recv_sock)
11829       closesocket(_server_acception[index].recv_sock);
11830    if (_server_acception[index].send_sock)
11831       closesocket(_server_acception[index].send_sock);
11832    if (_server_acception[index].event_sock)
11833       closesocket(_server_acception[index].event_sock);
11834 
11835    /* free TCP cache */
11836    M_FREE(_server_acception[index].net_buffer);
11837    _server_acception[index].net_buffer = NULL;
11838 
11839    /* mark this entry as invalid */
11840    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
11841 
11842    /* signal caller a shutdonw */
11843    if (status == RPC_SHUTDOWN)
11844       return status;
11845 
11846    /* don't abort if other than main connection is broken */
11847    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11848       return SS_SUCCESS;
11849 
11850    return status;
11851 }
11852 
11853 
11854 /********************************************************************/
11855 INT rpc_server_shutdown(void)
11856 /********************************************************************\
11857 
11858   Routine: rpc_server_shutdown
11859 
11860   Purpose: Shutdown RPC server, abort all connections
11861 
11862   Input:
11863     none
11864 
11865   Output:
11866     none
11867 
11868   Function value:
11869     RPC_SUCCESS             Successful completion
11870 
11871 \********************************************************************/
11872 {
11873    INT i;
11874    struct linger ling;
11875 
11876    /* close all open connections */
11877    for (i = 0; i < MAX_RPC_CONNECTION; i++)
11878       if (_server_acception[i].recv_sock != 0) {
11879          /* lingering needed for PCTCP */
11880          ling.l_onoff = 1;
11881          ling.l_linger = 0;
11882          setsockopt(_server_acception[i].recv_sock,
11883                     SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11884          closesocket(_server_acception[i].recv_sock);
11885 
11886          if (_server_acception[i].send_sock) {
11887             setsockopt(_server_acception[i].send_sock,
11888                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11889             closesocket(_server_acception[i].send_sock);
11890          }
11891 
11892          if (_server_acception[i].event_sock) {
11893             setsockopt(_server_acception[i].event_sock,
11894                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11895             closesocket(_server_acception[i].event_sock);
11896          }
11897 
11898          _server_acception[i].recv_sock = 0;
11899          _server_acception[i].send_sock = 0;
11900          _server_acception[i].event_sock = 0;
11901       }
11902 
11903    if (_lsock) {
11904       closesocket(_lsock);
11905       _lsock = 0;
11906       _server_registered = FALSE;
11907    }
11908 
11909    /* free suspend structures */
11910    ss_suspend_exit();
11911 
11912    return RPC_SUCCESS;
11913 }
11914 
11915 
11916 /********************************************************************/
11917 INT rpc_check_channels(void)
11918 /********************************************************************\
11919 
11920   Routine: rpc_check_channels
11921 
11922   Purpose: Check open rpc channels by sending watchdog messages
11923 
11924   Input:
11925     none
11926 
11927   Output:
11928     none
11929 
11930   Function value:
11931     RPC_SUCCESS             Channel is still alive
11932     RPC_NET_ERROR           Connection is broken
11933 
11934 \********************************************************************/
11935 {
11936    INT status, index, i, convert_flags;
11937    NET_COMMAND nc;
11938    fd_set readfds;
11939    struct timeval timeout;
11940 
11941    for (index = 0; index < MAX_RPC_CONNECTION; index++) {
11942       if (_server_acception[index].recv_sock &&
11943           _server_acception[index].tid == ss_gettid() &&
11944           _server_acception[index].watchdog_timeout &&
11945           (ss_millitime() - _server_acception[index].last_activity >
11946            (DWORD) _server_acception[index].watchdog_timeout)) {
11947 /* printf("Send watchdog message to %s on %s\n",
11948                 _server_acception[index].prog_name,
11949                 _server_acception[index].host_name); */
11950 
11951          /* send a watchdog message */
11952          nc.header.routine_id = MSG_WATCHDOG;
11953          nc.header.param_size = 0;
11954 
11955          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
11956          if (convert_flags) {
11957             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING,
11958                                convert_flags);
11959             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING,
11960                                convert_flags);
11961          }
11962 
11963          /* send the header to the client */
11964          i = send_tcp(_server_acception[index].send_sock,
11965                       (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
11966 
11967          if (i < 0)
11968             goto exit;
11969 
11970          /* make some timeout checking */
11971          FD_ZERO(&readfds);
11972          FD_SET(_server_acception[index].send_sock, &readfds);
11973          FD_SET(_server_acception[index].recv_sock, &readfds);
11974 
11975          timeout.tv_sec = _server_acception[index].watchdog_timeout / 1000;
11976          timeout.tv_usec = (_server_acception[index].watchdog_timeout % 1000) * 1000;
11977 
11978          do {
11979             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
11980 
11981             /* if an alarm signal was cought, restart select with reduced timeout */
11982             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
11983                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
11984 
11985          } while (status == -1);        /* dont return if an alarm signal was cought */
11986 
11987          if (!FD_ISSET(_server_acception[index].send_sock, &readfds) &&
11988              !FD_ISSET(_server_acception[index].recv_sock, &readfds))
11989             goto exit;
11990 
11991          /* receive result on send socket */
11992          if (FD_ISSET(_server_acception[index].send_sock, &readfds)) {
11993             i = recv_tcp(_server_acception[index].send_sock, (char *) &nc, sizeof(nc), 0);
11994             if (i <= 0)
11995                goto exit;
11996          }
11997       }
11998    }
11999 
12000    return RPC_SUCCESS;
12001 
12002  exit:
12003 
12004    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
12005           _server_acception[index].host_name,
12006           _server_acception[index].prog_name,
12007           _server_acception[index].watchdog_timeout / 1000);
12008 
12009    /* disconnect from experiment */
12010    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12011       cm_disconnect_experiment();
12012 
12013    /* close server connection */
12014    if (_server_acception[index].recv_sock)
12015       closesocket(_server_acception[index].recv_sock);
12016    if (_server_acception[index].send_sock)
12017       closesocket(_server_acception[index].send_sock);
12018    if (_server_acception[index].event_sock)
12019       closesocket(_server_acception[index].event_sock);
12020 
12021    /* free TCP cache */
12022    M_FREE(_server_acception[index].net_buffer);
12023    _server_acception[index].net_buffer = NULL;
12024 
12025    /* mark this entry as invalid */
12026    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12027 
12028    return RPC_NET_ERROR;
12029 }
12030 
12031 /**dox***************************************************************/
12032 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12033 
12034 /**dox***************************************************************/
12035                             /** @} *//* end of rpcfunctionc */
12036 
12037 /**dox***************************************************************/
12038 /** @addtogroup bkfunctionc
12039  *  
12040  *  @{  */
12041 
12042 /********************************************************************\
12043 *                                                                    *
12044 *                 Bank functions                                     *
12045 *                                                                    *
12046 \********************************************************************/
12047 
12048 /********************************************************************/
12049 /**
12050 Initializes an event for Midas banks structure.
12051 Before banks can be created in an event, bk_init() has to be called first.
12052 @param event pointer to the area of event
12053 */
12054 void bk_init(void *event)
12055 {
12056    ((BANK_HEADER *) event)->data_size = 0;
12057    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
12058 }
12059 
12060 /**dox***************************************************************/
12061 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12062 
12063 /********************************************************************/
12064 BOOL bk_is32(void *event)
12065 /********************************************************************\
12066 
12067   Routine: bk_is32
12068 
12069   Purpose: Return true if banks inside event are 32-bit banks
12070 
12071   Input:
12072     void   *event           pointer to the event
12073 
12074   Output:
12075     none
12076 
12077   Function value:
12078     none
12079 
12080 \********************************************************************/
12081 {
12082    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12083 }
12084 
12085 /**dox***************************************************************/
12086 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12087 
12088 /********************************************************************/
12089 /**
12090 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12091 Before banks can be created in an event, bk_init32() has to be called first.
12092 @param event pointer to the area of event
12093 @return void
12094 */
12095 void bk_init32(void *event)
12096 {
12097    ((BANK_HEADER *) event)->data_size = 0;
12098    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12099 }
12100 
12101 /********************************************************************/
12102 /**
12103 Returns the size of an event containing banks.
12104 The total size of an event is the value returned by bk_size() plus the size
12105 of the event header (sizeof(EVENT_HEADER)).
12106 @param event pointer to the area of event
12107 @return number of bytes contained in data area of event
12108 */
12109 INT bk_size(void *event)
12110 {
12111    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12112 }
12113 
12114 /********************************************************************/
12115 /**
12116 Create a Midas bank.
12117 The data pointer pdata must be used as an address to fill a
12118 bank. It is incremented with every value written to the bank and finally points
12119 to a location just after the last byte of the bank. It is then passed to
12120 the function bk_close() to finish the bank creation.
12121 \code
12122 INT *pdata;
12123 bk_init(pevent);
12124 bk_create(pevent, "ADC0", TID_INT, &pdata);
12125 *pdata++ = 123
12126 *pdata++ = 456
12127 bk_close(pevent, pdata);
12128 \endcode
12129 @param event pointer to the data area
12130 @param name of the bank, must be exactly 4 charaters
12131 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12132 midas.h
12133 @param pdata pointer to the data area of the newly created bank
12134 @return void
12135 */
12136 void bk_create(void *event, const char *name, WORD type, void *pdata)
12137 {
12138    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12139       BANK32 *pbk32;
12140 
12141       pbk32 =
12142           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12143                       ((BANK_HEADER *) event)->data_size);
12144       strncpy(pbk32->name, name, 4);
12145       pbk32->type = type;
12146       pbk32->data_size = 0;
12147       *((void **) pdata) = pbk32 + 1;
12148    } else {
12149       BANK *pbk;
12150 
12151       pbk =
12152           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12153                     ((BANK_HEADER *) event)->data_size);
12154       strncpy(pbk->name, name, 4);
12155       pbk->type = type;
12156       pbk->data_size = 0;
12157       *((void **) pdata) = pbk + 1;
12158    }
12159 }
12160 
12161 
12162 
12163 /**dox***************************************************************/
12164 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12165 
12166 /********************************************************************/
12167 int bk_delete(void *event, const char *name)
12168 /********************************************************************\
12169 
12170   Routine: bk_delete
12171 
12172   Purpose: Delete a MIDAS bank inside an event
12173 
12174   Input:
12175     void   *event           pointer to the event
12176     char   *name            Name of bank (exactly four letters)
12177 
12178   Function value:
12179     CM_SUCCESS              Bank has been deleted
12180     0                       Bank has not been found
12181 
12182 \********************************************************************/
12183 {
12184    BANK *pbk;
12185    BANK32 *pbk32;
12186    DWORD dname;
12187    int remaining;
12188 
12189    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12190       /* locate bank */
12191       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12192       strncpy((char *) &dname, name, 4);
12193       do {
12194          if (*((DWORD *) pbk32->name) == dname) {
12195             /* bank found, delete it */
12196             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
12197                          sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) +
12198                                                  ALIGN8(pbk32->data_size));
12199 
12200             /* reduce total event size */
12201             ((BANK_HEADER *) event)->data_size -=
12202                 sizeof(BANK32) + ALIGN8(pbk32->data_size);
12203 
12204             /* copy remaining bytes */
12205             if (remaining > 0)
12206                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12207             return CM_SUCCESS;
12208          }
12209 
12210          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12211       } while ((DWORD) ((char *) pbk32 - (char *) event) <
12212                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12213    } else {
12214       /* locate bank */
12215       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12216       strncpy((char *) &dname, name, 4);
12217       do {
12218          if (*((DWORD *) pbk->name) == dname) {
12219             /* bank found, delete it */
12220             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
12221                          sizeof(BANK_HEADER)) - ((char *) (pbk + 1) +
12222                                                  ALIGN8(pbk->data_size));
12223 
12224             /* reduce total event size */
12225             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
12226 
12227             /* copy remaining bytes */
12228             if (remaining > 0)
12229                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
12230             return CM_SUCCESS;
12231          }
12232 
12233          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12234       } while ((DWORD) ((char *) pbk - (char *) event) <
12235                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12236    }
12237 
12238    return 0;
12239 }
12240 
12241 /**dox***************************************************************/
12242 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12243 
12244 /********************************************************************/
12245 /**
12246 Close the Midas bank priviously created by bk_create().
12247 The data pointer pdata must be obtained by bk_create() and
12248 used as an address to fill a bank. It is incremented with every value written
12249 to the bank and finally points to a location just after the last byte of the
12250 bank. It is then passed to bk_close() to finish the bank creation
12251 @param event pointer to current composed event
12252 @param pdata  pointer to the data
12253 @return number of bytes contained in bank
12254 */
12255 INT bk_close(void *event, void *pdata)
12256 {
12257    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12258       BANK32 *pbk32;
12259 
12260       pbk32 =
12261           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12262                       ((BANK_HEADER *) event)->data_size);
12263       pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
12264       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12265          printf("Warning: bank %c%c%c%c has zero size\n",
12266                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
12267       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
12268       return pbk32->data_size;
12269    } else {
12270       BANK *pbk;
12271 
12272       pbk =
12273           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12274                     ((BANK_HEADER *) event)->data_size);
12275       pbk->data_size = (WORD) ((char *) pdata - (char *) (pbk + 1));
12276       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12277          printf("Warning: bank %c%c%c%c has zero size\n",
12278                 pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12279       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
12280       return pbk->data_size;
12281    }
12282 }
12283 
12284 /********************************************************************/
12285 /**
12286 Extract the MIDAS bank name listing of an event.
12287 The bklist should be dimensioned with STRING_BANKLIST_MAX
12288 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12289 \code
12290 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12291 {
12292   INT    n_adc, nbanks;
12293   WORD   *pdata;
12294   char   banklist[STRING_BANKLIST_MAX];
12295 
12296   // Display # of banks and list of banks in the event
12297   nbanks = bk_list(pevent, banklist);
12298   printf("#banks:%d List:%s\n", nbanks, banklist);
12299 
12300   // look for ADC0 bank, return if not present
12301   n_adc = bk_locate(pevent, "ADC0", &pdata);
12302   ...
12303 }
12304 \endcode
12305 @param event pointer to current composed event
12306 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12307 @return number of bank found in this event.
12308 */
12309 INT bk_list(void *event, char *bklist)
12310 {                               /* Full event */
12311    INT nbk, size;
12312    BANK *pmbk = NULL;
12313    BANK32 *pmbk32 = NULL;
12314    char *pdata;
12315 
12316    /* compose bank list */
12317    bklist[0] = 0;
12318    nbk = 0;
12319    do {
12320       /* scan all banks for bank name only */
12321       if (bk_is32(event)) {
12322          size = bk_iterate32(event, &pmbk32, &pdata);
12323          if (pmbk32 == NULL)
12324             break;
12325       } else {
12326          size = bk_iterate(event, &pmbk, &pdata);
12327          if (pmbk == NULL)
12328             break;
12329       }
12330       nbk++;
12331 
12332       if (nbk > BANKLIST_MAX) {
12333          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
12334          return (nbk - 1);
12335       }
12336       if (bk_is32(event))
12337          strncat(bklist, (char *) pmbk32->name, 4);
12338       else
12339          strncat(bklist, (char *) pmbk->name, 4);
12340    }
12341    while (1);
12342    return (nbk);
12343 }
12344 
12345 /********************************************************************/
12346 /**
12347 Locates a MIDAS bank of given name inside an event.
12348 @param event pointer to current composed event
12349 @param name bank name to look for
12350 @param pdata pointer to data area of bank, NULL if bank not found
12351 @return number of values inside the bank
12352 */
12353 INT bk_locate(void *event, const char *name, void *pdata)
12354 {
12355    BANK *pbk;
12356    BANK32 *pbk32;
12357    DWORD dname;
12358 
12359    if (bk_is32(event)) {
12360       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12361       strncpy((char *) &dname, name, 4);
12362       while ((DWORD) ((char *) pbk32 - (char *) event) <
12363              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12364          if (*((DWORD *) pbk32->name) == dname) {
12365             *((void **) pdata) = pbk32 + 1;
12366             if (tid_size[pbk32->type & 0xFF] == 0)
12367                return pbk32->data_size;
12368             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
12369          }
12370          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12371       }
12372    } else {
12373       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12374       strncpy((char *) &dname, name, 4);
12375       while ((DWORD) ((char *) pbk - (char *) event) <
12376              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12377          if (*((DWORD *) pbk->name) == dname) {
12378             *((void **) pdata) = pbk + 1;
12379             if (tid_size[pbk->type & 0xFF] == 0)
12380                return pbk->data_size;
12381             return pbk->data_size / tid_size[pbk->type & 0xFF];
12382          }
12383          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12384       }
12385 
12386    }
12387 
12388    /* bank not found */
12389    *((void **) pdata) = NULL;
12390    return 0;
12391 }
12392 
12393 /********************************************************************/
12394 /**
12395 Finds a MIDAS bank of given name inside an event.
12396 @param pbkh pointer to current composed event
12397 @param name bank name to look for
12398 @param bklen number of elemtents in bank
12399 @param bktype bank type, one of TID_xxx
12400 @param pdata pointer to data area of bank, NULL if bank not found
12401 @return 1 if bank found, 0 otherwise
12402 */
12403 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype,
12404             void **pdata)
12405 {
12406    BANK *pbk;
12407    BANK32 *pbk32;
12408    DWORD dname;
12409 
12410    if (bk_is32(pbkh)) {
12411       pbk32 = (BANK32 *) (pbkh + 1);
12412       strncpy((char *) &dname, name, 4);
12413       do {
12414          if (*((DWORD *) pbk32->name) == dname) {
12415             *((void **) pdata) = pbk32 + 1;
12416             if (tid_size[pbk32->type & 0xFF] == 0)
12417                *bklen = pbk32->data_size;
12418             else
12419                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
12420 
12421             *bktype = pbk32->type;
12422             return 1;
12423          }
12424          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12425       } while ((DWORD) ((char *) pbk32 - (char *) pbkh) <
12426                pbkh->data_size + sizeof(BANK_HEADER));
12427    } else {
12428       pbk = (BANK *) (pbkh + 1);
12429       strncpy((char *) &dname, name, 4);
12430       do {
12431          if (*((DWORD *) pbk->name) == dname) {
12432             *((void **) pdata) = pbk + 1;
12433             if (tid_size[pbk->type & 0xFF] == 0)
12434                *bklen = pbk->data_size;
12435             else
12436                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
12437 
12438             *bktype = pbk->type;
12439             return 1;
12440          }
12441          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12442       } while ((DWORD) ((char *) pbk - (char *) pbkh) <
12443                pbkh->data_size + sizeof(BANK_HEADER));
12444    }
12445 
12446    /* bank not found */
12447    *((void **) pdata) = NULL;
12448    return 0;
12449 }
12450 
12451 /********************************************************************/
12452 /**
12453 Iterates through banks inside an event.
12454 The function can be used to enumerate all banks of an event.
12455 The returned pointer to the bank header has following structure:
12456 \code
12457 typedef struct {
12458 char   name[4];
12459 WORD   type;
12460 WORD   data_size;
12461 } BANK;
12462 \endcode
12463 where type is a TID_xxx value and data_size the size of the bank in bytes.
12464 \code
12465 BANK *pbk;
12466 INT  size;
12467 void *pdata;
12468 char name[5];
12469 pbk = NULL;
12470 do
12471 {
12472  size = bk_iterate(event, &pbk, &pdata);
12473  if (pbk == NULL)
12474   break;
12475  *((DWORD *)name) = *((DWORD *)(pbk)->name);
12476  name[4] = 0;
12477  printf("bank %s found\n", name);
12478 } while(TRUE);
12479 \endcode
12480 @param event Pointer to data area of event.
12481 @param pbk pointer to the bank header, must be NULL for the first call to
12482 this function.
12483 @param pdata Pointer to the bank header, must be NULL for the first
12484 call to this function
12485 @return Size of bank in bytes
12486 */
12487 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
12488 {
12489    if (*pbk == NULL)
12490       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12491    else
12492       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12493 
12494    *((void **) pdata) = (*pbk) + 1;
12495 
12496    if ((DWORD) ((char *) *pbk - (char *) event) >=
12497        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12498       *pbk = *((BANK **) pdata) = NULL;
12499       return 0;
12500    }
12501 
12502    return (*pbk)->data_size;
12503 }
12504 
12505 
12506 /**dox***************************************************************/
12507 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12508 
12509 /********************************************************************/
12510 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
12511 /********************************************************************\
12512 
12513   Routine: bk_iterate
12514 
12515   Purpose: Iterate through 32 bit MIDAS banks inside an event
12516 
12517   Input:
12518     void   *event           pointer to the event
12519     BANK   **pbk32          must be NULL for the first call to bk_iterate
12520 
12521   Output:
12522     BANK   **pbk            pointer to the bank header
12523     void   *pdata           pointer to data area of the bank
12524 
12525   Function value:
12526     INT    size of the bank in bytes
12527 
12528 \********************************************************************/
12529 {
12530    if (*pbk == NULL)
12531       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
12532    else
12533       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12534 
12535    *((void **) pdata) = (*pbk) + 1;
12536 
12537    if ((DWORD) ((char *) *pbk - (char *) event) >=
12538        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12539       *pbk = *((BANK32 **) pdata) = NULL;
12540       return 0;
12541    }
12542 
12543    return (*pbk)->data_size;
12544 }
12545 
12546 /**dox***************************************************************/
12547 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12548 
12549 /********************************************************************/
12550 /**
12551 Swaps bytes from little endian to big endian or vice versa for a whole event.
12552 
12553 An event contains a flag which is set by bk_init() to identify
12554 the endian format of an event. If force is FALSE, this flag is evaluated and
12555 the event is only swapped if it is in the "wrong" format for this system.
12556 An event can be swapped to the "wrong" format on purpose for example by a
12557 front-end which wants to produce events in a "right" format for a back-end
12558 analyzer which has different byte ordering.
12559 @param event pointer to data area of event
12560 @param force If TRUE, the event is always swapped, if FALSE, the event
12561 is only swapped if it is in the wrong format.
12562 @return 1==event has been swap, 0==event has not been swapped.
12563 */
12564 INT bk_swap(void *event, BOOL force)
12565 {
12566    BANK_HEADER *pbh;
12567    BANK *pbk;
12568    BANK32 *pbk32;
12569    void *pdata;
12570    WORD type;
12571    BOOL b32;
12572 
12573    pbh = (BANK_HEADER *) event;
12574 
12575    /* only swap if flags in high 16-bit */
12576    if (pbh->flags < 0x10000 && !force)
12577       return 0;
12578 
12579    /* swap bank header */
12580    DWORD_SWAP(&pbh->data_size);
12581    DWORD_SWAP(&pbh->flags);
12582 
12583    /* check for 32bit banks */
12584    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
12585 
12586    pbk = (BANK *) (pbh + 1);
12587    pbk32 = (BANK32 *) pbk;
12588 
12589    /* scan event */
12590    while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
12591       /* swap bank header */
12592       if (b32) {
12593          DWORD_SWAP(&pbk32->type);
12594          DWORD_SWAP(&pbk32->data_size);
12595          pdata = pbk32 + 1;
12596          type = (WORD) pbk32->type;
12597       } else {
12598          WORD_SWAP(&pbk->type);
12599          WORD_SWAP(&pbk->data_size);
12600          pdata = pbk + 1;
12601          type = pbk->type;
12602       }
12603 
12604       /* pbk points to next bank */
12605       if (b32) {
12606          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12607          pbk = (BANK *) pbk32;
12608       } else {
12609          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12610          pbk32 = (BANK32 *) pbk;
12611       }
12612 
12613       switch (type) {
12614       case TID_WORD:
12615       case TID_SHORT:
12616          while ((char *) pdata < (char *) pbk) {
12617             WORD_SWAP(pdata);
12618             pdata = (void *) (((WORD *) pdata) + 1);
12619          }
12620          break;
12621 
12622       case TID_DWORD:
12623       case TID_INT:
12624       case TID_BOOL:
12625       case TID_FLOAT:
12626          while ((char *) pdata < (char *) pbk) {
12627             DWORD_SWAP(pdata);
12628             pdata = (void *) (((DWORD *) pdata) + 1);
12629          }
12630          break;
12631 
12632       case TID_DOUBLE:
12633          while ((char *) pdata < (char *) pbk) {
12634             QWORD_SWAP(pdata);
12635             pdata = (void *) (((double *) pdata) + 1);
12636          }
12637          break;
12638       }
12639    }
12640 
12641    return CM_SUCCESS;
12642 }
12643 
12644 /**dox***************************************************************/
12645                             /** @} *//* end of bkfunctionc */
12646 
12647 /**dox***************************************************************/
12648 /** @addtogroup hsfunctionc
12649  *  
12650  *  @{  */
12651 
12652 #if !defined(OS_VXWORKS)
12653 /********************************************************************\
12654 *                                                                    *
12655 *                 History functions                                  *
12656 *                                                                    *
12657 \********************************************************************/
12658 
12659 /**dox***************************************************************/
12660 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12661 
12662 static HISTORY *_history;
12663 static INT _history_entries = 0;
12664 static char _hs_path_name[MAX_STRING_LENGTH];
12665 
12666 /**dox***************************************************************/
12667 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12668 
12669 /********************************************************************/
12670 /**
12671 Sets the path for future history file accesses. Should
12672 be called before any other history function is called.
12673 @param path             Directory where history files reside
12674 @return HS_SUCCESS
12675 */
12676 INT hs_set_path(char *path)
12677 {
12678    /* set path locally and remotely */
12679    if (rpc_is_remote())
12680       rpc_call(RPC_HS_SET_PATH, path);
12681 
12682    strcpy(_hs_path_name, path);
12683 
12684    /* check for trailing directory seperator */
12685    if (strlen(_hs_path_name) > 0 &&
12686        _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
12687       strcat(_hs_path_name, DIR_SEPARATOR_STR);
12688 
12689    return HS_SUCCESS;
12690 }
12691 
12692 
12693 /********************************************************************/
12694 /**
12695 Open history file belonging to certain date. Internal use
12696            only.
12697 @param ltime          Date for which a history file should be opened.
12698 @param suffix         File name suffix like "hst", "idx", "idf"
12699 @param mode           R/W access mode
12700 @param fh             File handle
12701 @return HS_SUCCESS
12702 */
12703 INT hs_open_file(time_t ltime, char *suffix, INT mode, int *fh)
12704 {
12705    struct tm *tms;
12706    char file_name[256];
12707    time_t ttime;
12708 
12709    /* generate new file name YYMMDD.xxx */
12710 #if !defined(OS_VXWORKS)
12711 #if !defined(OS_VMS)
12712    tzset();
12713 #endif
12714 #endif
12715    ttime = (time_t) ltime;
12716    tms = localtime(&ttime);
12717 
12718    sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name,
12719            tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
12720 
12721    /* open file, add O_BINARY flag for Windows NT */
12722    *fh = open(file_name, mode | O_BINARY, 0644);
12723 
12724    return HS_SUCCESS;
12725 }
12726 
12727 /**dox***************************************************************/
12728 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12729 
12730 /********************************************************************/
12731 INT hs_gen_index(DWORD ltime)
12732 /********************************************************************\
12733 
12734   Routine: hs_gen_index
12735 
12736   Purpose: Regenerate index files ("idx" and "idf" files) for a given
12737            history file ("hst"). Interal use only.
12738 
12739   Input:
12740     time_t ltime            Date for which a history file should
12741                             be analyzed.
12742 
12743   Output:
12744     none
12745 
12746   Function value:
12747     HS_SUCCESS              Successful completion
12748     HS_FILE_ERROR           Index files cannot be created
12749 
12750 \********************************************************************/
12751 {
12752    char event_name[NAME_LENGTH];
12753    int fh, fhd, fhi;
12754    INT n;
12755    HIST_RECORD rec;
12756    INDEX_RECORD irec;
12757    DEF_RECORD def_rec;
12758 
12759    printf("Recovering index files...\n");
12760 
12761    if (ltime == 0)
12762       ltime = time(NULL);
12763 
12764    /* open new index file */
12765    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
12766    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
12767 
12768    if (fhd < 0 || fhi < 0) {
12769       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
12770       return HS_FILE_ERROR;
12771    }
12772 
12773    /* open history file */
12774    hs_open_file(ltime, "hst", O_RDONLY, &fh);
12775    if (fh < 0)
12776       return HS_FILE_ERROR;
12777    lseek(fh, 0, SEEK_SET);
12778 
12779    /* loop over file records in .hst file */
12780    do {
12781       n = read(fh, (char *) &rec, sizeof(rec));
12782       if (n < sizeof(rec))
12783          break;
12784 
12785       /* check if record type is definition */
12786       if (rec.record_type == RT_DEF) {
12787          /* read name */
12788          read(fh, event_name, sizeof(event_name));
12789 
12790          printf("Event definition %s, ID %d\n", event_name, rec.event_id);
12791 
12792          /* write definition index record */
12793          def_rec.event_id = rec.event_id;
12794          memcpy(def_rec.event_name, event_name, sizeof(event_name));
12795          def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
12796          write(fhd, (char *) &def_rec, sizeof(def_rec));
12797 
12798          /* skip tags */
12799          lseek(fh, rec.data_size, SEEK_CUR);
12800       } else {
12801          /* write index record */
12802          irec.event_id = rec.event_id;
12803          irec.time = rec.time;
12804          irec.offset = TELL(fh) - sizeof(rec);
12805          write(fhi, (char *) &irec, sizeof(irec));
12806 
12807          /* printf("ID %d, %s\n", rec.event_id, ctime((const time_t *)&irec.time)+4); */
12808 
12809          /* skip data */
12810          lseek(fh, rec.data_size, SEEK_CUR);
12811       }
12812 
12813    } while (TRUE);
12814 
12815    close(fh);
12816    close(fhi);
12817    close(fhd);
12818 
12819    printf("...done.\n");
12820 
12821    return HS_SUCCESS;
12822 }
12823 
12824 
12825 /********************************************************************/
12826 INT hs_search_file(DWORD * ltime, INT direction)
12827 /********************************************************************\
12828 
12829   Routine: hs_search_file
12830 
12831   Purpose: Search an history file for a given date. If not found,
12832            look for files after date (direction==1) or before date
12833            (direction==-1) up to one year.
12834 
12835   Input:
12836     DWORD  *ltime           Date of history file
12837     INT    direction        Search direction
12838 
12839   Output:
12840     DWORD  *ltime           Date of history file found
12841 
12842   Function value:
12843     HS_SUCCESS              Successful completion
12844     HS_FILE_ERROR           No file found
12845 
12846 \********************************************************************/
12847 {
12848    time_t lt;
12849    int fh, fhd, fhi;
12850    struct tm *tms;
12851 
12852    if (*ltime == 0)
12853       *ltime = time(NULL);
12854 
12855    lt = (time_t) * ltime;
12856    do {
12857       /* try to open history file for date "lt" */
12858       hs_open_file(lt, "hst", O_RDONLY, &fh);
12859 
12860       /* if not found, look for next day */
12861       if (fh < 0)
12862          lt += direction * 3600 * 24;
12863 
12864       /* stop if more than a year before starting point or in the future */
12865    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 && lt < time(NULL));
12866 
12867    if (fh < 0)
12868       return HS_FILE_ERROR;
12869 
12870    if (lt != *ltime) {
12871       /* if switched to new day, set start_time to 0:00 */
12872       tms = localtime(&lt);
12873       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
12874       *ltime = mktime(tms);
12875    }
12876 
12877    /* check if index files are there */
12878    hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
12879    hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
12880 
12881    close(fh);
12882    close(fhd);
12883    close(fhi);
12884 
12885    /* generate them if not */
12886    if (fhd < 0 || fhi < 0)
12887       hs_gen_index(*ltime);
12888 
12889    return HS_SUCCESS;
12890 }
12891 
12892 
12893 /********************************************************************/
12894 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
12895 /********************************************************************\
12896 
12897   Routine: hs_define_evnet
12898 
12899   Purpose: Define a new event for which a history should be recorded.
12900            This routine must be called before any call to
12901            hs_write_event. It also should be called if the definition
12902            of the event has changed.
12903 
12904            The event definition is written directly to the history
12905            file. If the definition is identical to a previous
12906            definition, it is not written to the file.
12907 
12908 
12909   Input:
12910     DWORD  event_id         ID for this event. Must be unique.
12911     char   name             Name of this event
12912     TAG    tag              Tag list containing names and types of
12913                             variables in this event.
12914     DWORD  size             Size of tag array
12915 
12916   Output:
12917     <none>
12918 
12919   Function value:
12920     HS_SUCCESS              Successful completion
12921     HS_NO_MEMEORY           Out of memory
12922     HS_FILE_ERROR           Cannot open history file
12923 
12924 \********************************************************************/
12925 {
12926 /* History events are only written locally (?)
12927 
12928   if (rpc_is_remote())
12929     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
12930 */
12931    {
12932       HIST_RECORD rec, prev_rec;
12933       DEF_RECORD def_rec;
12934       char str[256], event_name[NAME_LENGTH], *buffer;
12935       int fh, fhi, fhd;
12936       INT i, n, len, index;
12937       struct tm *tmb;
12938 
12939       /* allocate new space for the new history descriptor */
12940       if (_history_entries == 0) {
12941          _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
12942          memset(_history, 0, sizeof(HISTORY));
12943          if (_history == NULL)
12944             return HS_NO_MEMORY;
12945 
12946          _history_entries = 1;
12947          index = 0;
12948       } else {
12949          /* check if history already open */
12950          for (i = 0; i < _history_entries; i++)
12951             if (_history[i].event_id == event_id)
12952                break;
12953 
12954          /* if not found, create new one */
12955          if (i == _history_entries) {
12956             _history =
12957                 (HISTORY *) realloc(_history, sizeof(HISTORY) * (_history_entries + 1));
12958             memset(&_history[_history_entries], 0, sizeof(HISTORY));
12959 
12960             _history_entries++;
12961             if (_history == NULL) {
12962                _history_entries--;
12963                return HS_NO_MEMORY;
12964             }
12965          }
12966          index = i;
12967       }
12968 
12969       /* assemble definition record header */
12970       rec.record_type = RT_DEF;
12971       rec.event_id = event_id;
12972       rec.time = time(NULL);
12973       rec.data_size = size;
12974       strncpy(event_name, name, NAME_LENGTH);
12975 
12976       /* pad tag names with zeos */
12977       for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
12978          len = strlen(tag[i].name);
12979          memset(tag[i].name + len, 0, NAME_LENGTH - len);
12980       }
12981 
12982       /* if history structure not set up, do so now */
12983       if (!_history[index].hist_fh) {
12984          /* open history file */
12985          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
12986          if (fh < 0)
12987             return HS_FILE_ERROR;
12988 
12989          /* open index files */
12990          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
12991          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
12992          lseek(fh, 0, SEEK_END);
12993          lseek(fhi, 0, SEEK_END);
12994          lseek(fhd, 0, SEEK_END);
12995 
12996          /* regenerate index if missing */
12997          if (TELL(fh) > 0 && TELL(fhd) == 0) {
12998             close(fh);
12999             close(fhi);
13000             close(fhd);
13001             hs_gen_index(rec.time);
13002             hs_open_file(rec.time, "hst", O_RDWR, &fh);
13003             hs_open_file(rec.time, "idx", O_RDWR, &fhi);
13004             hs_open_file(rec.time, "idf", O_RDWR, &fhd);
13005             lseek(fh, 0, SEEK_END);
13006             lseek(fhi, 0, SEEK_END);
13007             lseek(fhd, 0, SEEK_END);
13008          }
13009 
13010          tmb = localtime((const time_t *) &rec.time);
13011          tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
13012 
13013          /* setup history structure */
13014          _history[index].hist_fh = fh;
13015          _history[index].index_fh = fhi;
13016          _history[index].def_fh = fhd;
13017          _history[index].def_offset = TELL(fh);
13018          _history[index].event_id = event_id;
13019          strcpy(_history[index].event_name, event_name);
13020          _history[index].base_time = mktime(tmb);
13021          _history[index].n_tag = size / sizeof(TAG);
13022          _history[index].tag = (TAG *) M_MALLOC(size);
13023          memcpy(_history[index].tag, tag, size);
13024 
13025          /* search previous definition */
13026          n = TELL(fhd) / sizeof(def_rec);
13027          def_rec.event_id = 0;
13028          for (i = n - 1; i >= 0; i--) {
13029             lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13030             read(fhd, (char *) &def_rec, sizeof(def_rec));
13031             if (def_rec.event_id == event_id)
13032                break;
13033          }
13034          lseek(fhd, 0, SEEK_END);
13035 
13036          /* if definition found, compare it with new one */
13037          if (def_rec.event_id == event_id) {
13038             buffer = (char *) M_MALLOC(size);
13039             memset(buffer, 0, size);
13040 
13041             lseek(fh, def_rec.def_offset, SEEK_SET);
13042             read(fh, (char *) &prev_rec, sizeof(prev_rec));
13043             read(fh, str, NAME_LENGTH);
13044             read(fh, buffer, size);
13045             lseek(fh, 0, SEEK_END);
13046 
13047             if (prev_rec.data_size != size ||
13048                 strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13049                /* write definition to history file */
13050                write(fh, (char *) &rec, sizeof(rec));
13051                write(fh, event_name, NAME_LENGTH);
13052                write(fh, (char *) tag, size);
13053 
13054                /* write index record */
13055                def_rec.event_id = event_id;
13056                memcpy(def_rec.event_name, event_name, sizeof(event_name));
13057                def_rec.def_offset = _history[index].def_offset;
13058                write(fhd, (char *) &def_rec, sizeof(def_rec));
13059             } else
13060                /* definition identical, just remember old offset */
13061                _history[index].def_offset = def_rec.def_offset;
13062 
13063             M_FREE(buffer);
13064          } else {
13065             /* write definition to history file */
13066             write(fh, (char *) &rec, sizeof(rec));
13067             write(fh, event_name, NAME_LENGTH);
13068             write(fh, (char *) tag, size);
13069 
13070             /* write definition index record */
13071             def_rec.event_id = event_id;
13072             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13073             def_rec.def_offset = _history[index].def_offset;
13074             write(fhd, (char *) &def_rec, sizeof(def_rec));
13075          }
13076       } else {
13077          fh = _history[index].hist_fh;
13078          fhd = _history[index].def_fh;
13079 
13080          /* compare definition with previous definition */
13081          buffer = (char *) M_MALLOC(size);
13082          memset(buffer, 0, size);
13083 
13084          lseek(fh, _history[index].def_offset, SEEK_SET);
13085          read(fh, (char *) &prev_rec, sizeof(prev_rec));
13086          read(fh, str, NAME_LENGTH);
13087          read(fh, buffer, size);
13088 
13089          lseek(fh, 0, SEEK_END);
13090          lseek(fhd, 0, SEEK_END);
13091 
13092          if (prev_rec.data_size != size ||
13093              strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13094             /* save new definition offset */
13095             _history[index].def_offset = TELL(fh);
13096 
13097             /* write definition to history file */
13098             write(fh, (char *) &rec, sizeof(rec));
13099             write(fh, event_name, NAME_LENGTH);
13100             write(fh, (char *) tag, size);
13101 
13102             /* write index record */
13103             def_rec.event_id = event_id;
13104             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13105             def_rec.def_offset = _history[index].def_offset;
13106             write(fhd, (char *) &def_rec, sizeof(def_rec));
13107          }
13108 
13109          M_FREE(buffer);
13110       }
13111 
13112    }
13113 
13114    return HS_SUCCESS;
13115 }
13116 
13117 
13118 /********************************************************************/
13119 INT hs_write_event(DWORD event_id, void *data, DWORD size)
13120 /********************************************************************\
13121 
13122   Routine: hs_write_event
13123 
13124   Purpose: Write an event to a history file.
13125 
13126   Input:
13127     DWORD  event_id         Event ID
13128     void   *data            Data buffer containing event
13129     DWORD  size             Data buffer size in bytes
13130 
13131   Output:
13132     none
13133                             future hs_write_event
13134 
13135   Function value:
13136     HS_SUCCESS              Successful completion
13137     HS_NO_MEMEORY           Out of memory
13138     HS_FILE_ERROR           Cannot write to history file
13139     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
13140 
13141 \********************************************************************/
13142 {
13143 /* history events are only written locally (?)
13144 
13145   if (rpc_is_remote())
13146     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
13147 */
13148    HIST_RECORD rec, drec;
13149    DEF_RECORD def_rec;
13150    INDEX_RECORD irec;
13151    int fh, fhi, fhd;
13152    INT index;
13153    struct tm tmb, tmr;
13154 
13155    /* find index to history structure */
13156    for (index = 0; index < _history_entries; index++)
13157       if (_history[index].event_id == event_id)
13158          break;
13159    if (index == _history_entries)
13160       return HS_UNDEFINED_EVENT;
13161 
13162    /* assemble record header */
13163    rec.record_type = RT_DATA;
13164    rec.event_id = _history[index].event_id;
13165    rec.time = time(NULL);
13166    rec.def_offset = _history[index].def_offset;
13167    rec.data_size = size;
13168 
13169    irec.event_id = _history[index].event_id;
13170    irec.time = rec.time;
13171 
13172    /* check if new day */
13173    memcpy(&tmr, localtime((const time_t *) &rec.time), sizeof(tmr));
13174    memcpy(&tmb, localtime((const time_t *) &_history[index].base_time), sizeof(tmb));
13175 
13176    if (tmr.tm_yday != tmb.tm_yday) {
13177       /* close current history file */
13178       close(_history[index].hist_fh);
13179       close(_history[index].def_fh);
13180       close(_history[index].index_fh);
13181 
13182       /* open new history file */
13183       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13184       if (fh < 0)
13185          return HS_FILE_ERROR;
13186 
13187       /* open new index file */
13188       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13189       if (fhi < 0)
13190          return HS_FILE_ERROR;
13191 
13192       /* open new definition index file */
13193       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13194       if (fhd < 0)
13195          return HS_FILE_ERROR;
13196 
13197       lseek(fh, 0, SEEK_END);
13198       lseek(fhi, 0, SEEK_END);
13199       lseek(fhd, 0, SEEK_END);
13200 
13201       /* remember new file handles */
13202       _history[index].hist_fh = fh;
13203       _history[index].index_fh = fhi;
13204       _history[index].def_fh = fhd;
13205 
13206       _history[index].def_offset = TELL(fh);
13207       rec.def_offset = _history[index].def_offset;
13208 
13209       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
13210       _history[index].base_time = mktime(&tmr);
13211 
13212       /* write definition from _history structure */
13213       drec.record_type = RT_DEF;
13214       drec.event_id = _history[index].event_id;
13215       drec.time = rec.time;
13216       drec.data_size = _history[index].n_tag * sizeof(TAG);
13217 
13218       write(fh, (char *) &drec, sizeof(drec));
13219       write(fh, _history[index].event_name, NAME_LENGTH);
13220       write(fh, (char *) _history[index].tag, drec.data_size);
13221 
13222       /* write definition index record */
13223       def_rec.event_id = _history[index].event_id;
13224       memcpy(def_rec.event_name, _history[index].event_name, sizeof(def_rec.event_name));
13225       def_rec.def_offset = _history[index].def_offset;
13226       write(fhd, (char *) &def_rec, sizeof(def_rec));
13227    }
13228 
13229    /* got to end of file */
13230    lseek(_history[index].hist_fh, 0, SEEK_END);
13231    irec.offset = TELL(_history[index].hist_fh);
13232 
13233    /* write record header */
13234    write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
13235 
13236    /* write data */
13237    write(_history[index].hist_fh, (char *) data, size);
13238 
13239    /* write index record */
13240    lseek(_history[index].index_fh, 0, SEEK_END);
13241    if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) < sizeof(irec))
13242       return HS_FILE_ERROR;
13243 
13244    return HS_SUCCESS;
13245 }
13246 
13247 
13248 /********************************************************************/
13249 INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size,
13250                    INT event_id[], DWORD * id_size)
13251 /********************************************************************\
13252 
13253   Routine: hs_enum_events
13254 
13255   Purpose: Enumerate events for a given date
13256 
13257   Input:
13258     DWORD  ltime            Date at which events should be enumerated
13259 
13260   Output:
13261     char   *event_name      Array containing event names
13262     DWORD  *name_size       Size of name array
13263     char   *event_id        Array containing event IDs
13264     DWORD  *id_size         Size of ID array
13265 
13266   Function value:
13267     HS_SUCCESS              Successful completion
13268     HS_NO_MEMEORY           Out of memory
13269     HS_FILE_ERROR           Cannot open history file
13270 
13271 \********************************************************************/
13272 {
13273    int fh, fhd;
13274    INT status, i, j, n;
13275    DEF_RECORD def_rec;
13276 
13277    if (rpc_is_remote())
13278       return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name, name_size, event_id,
13279                       id_size);
13280 
13281    /* search latest history file */
13282    status = hs_search_file(&ltime, -1);
13283    if (status != HS_SUCCESS) {
13284       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
13285       return HS_FILE_ERROR;
13286    }
13287 
13288    /* open history and definition files */
13289    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13290    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13291    if (fh < 0 || fhd < 0) {
13292       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
13293       return HS_FILE_ERROR;
13294    }
13295    lseek(fhd, 0, SEEK_SET);
13296 
13297    /* loop over definition index file */
13298    n = 0;
13299    do {
13300       /* read event definition */
13301       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13302       if (j < (int) sizeof(def_rec))
13303          break;
13304 
13305       /* look for existing entry for this event id */
13306       for (i = 0; i < n; i++)
13307          if (event_id[i] == (INT) def_rec.event_id) {
13308             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13309             break;
13310          }
13311 
13312       /* new entry found */
13313       if (i == n) {
13314          if (i * NAME_LENGTH > (INT) * name_size || i * sizeof(INT) > (INT) * id_size) {
13315             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
13316             close(fh);
13317             close(fhd);
13318             return HS_NO_MEMORY;
13319          }
13320 
13321          /* copy definition record */
13322          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13323          event_id[i] = def_rec.event_id;
13324          n++;
13325       }
13326    } while (TRUE);
13327 
13328    close(fh);
13329    close(fhd);
13330    *name_size = n * NAME_LENGTH;
13331    *id_size = n * sizeof(INT);
13332 
13333    return HS_SUCCESS;
13334 }
13335 
13336 
13337 /********************************************************************/
13338 INT hs_count_events(DWORD ltime, DWORD * count)
13339 /********************************************************************\
13340 
13341   Routine: hs_count_events
13342 
13343   Purpose: Count number of different events for a given date
13344 
13345   Input:
13346     DWORD  ltime            Date at which events should be counted
13347 
13348   Output:
13349     DWORD  *count           Number of different events found
13350 
13351   Function value:
13352     HS_SUCCESS              Successful completion
13353     HS_FILE_ERROR           Cannot open history file
13354 
13355 \********************************************************************/
13356 {
13357    int fh, fhd;
13358    INT status, i, j, n;
13359    DWORD *id;
13360    DEF_RECORD def_rec;
13361 
13362    if (rpc_is_remote())
13363       return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
13364 
13365    /* search latest history file */
13366    status = hs_search_file(&ltime, -1);
13367    if (status != HS_SUCCESS) {
13368       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13369       return HS_FILE_ERROR;
13370    }
13371 
13372    /* open history and definition files */
13373    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13374    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13375    if (fh < 0 || fhd < 0) {
13376       cm_msg(MERROR, "hs_count_events", "cannot open index files");
13377       return HS_FILE_ERROR;
13378    }
13379 
13380    /* allocate event id array */
13381    lseek(fhd, 0, SEEK_END);
13382    id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
13383    lseek(fhd, 0, SEEK_SET);
13384 
13385    /* loop over index file */
13386    n = 0;
13387    do {
13388       /* read definition index record */
13389       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13390       if (j < (int) sizeof(def_rec))
13391          break;
13392 
13393       /* look for existing entries */
13394       for (i = 0; i < n; i++)
13395          if (id[i] == def_rec.event_id)
13396             break;
13397 
13398       /* new entry found */
13399       if (i == n) {
13400          id[i] = def_rec.event_id;
13401          n++;
13402       }
13403    } while (TRUE);
13404 
13405 
13406    M_FREE(id);
13407    close(fh);
13408    close(fhd);
13409    *count = n;
13410 
13411    return HS_SUCCESS;
13412 }
13413 
13414 
13415 /********************************************************************/
13416 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
13417 /********************************************************************\
13418 
13419   Routine: hs_get_event_id
13420 
13421   Purpose: Return event ID for a given name. If event cannot be found
13422            in current definition file, go back in time until found
13423 
13424   Input:
13425     DWORD  ltime            Date at which event ID should be looked for
13426 
13427   Output:
13428     DWORD  *id              Event ID
13429 
13430   Function value:
13431     HS_SUCCESS              Successful completion
13432     HS_FILE_ERROR           Cannot open history file
13433     HS_UNDEFINED_EVENT      Event "name" not found
13434 
13435 \********************************************************************/
13436 {
13437    int fh, fhd;
13438    INT status, i;
13439    DWORD lt;
13440    DEF_RECORD def_rec;
13441 
13442    if (rpc_is_remote())
13443       return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
13444 
13445    /* search latest history file */
13446    if (ltime == 0)
13447       ltime = time(NULL);
13448 
13449    lt = ltime;
13450 
13451    do {
13452       status = hs_search_file(&lt, -1);
13453       if (status != HS_SUCCESS) {
13454          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13455          return HS_FILE_ERROR;
13456       }
13457 
13458       /* open history and definition files */
13459       hs_open_file(lt, "hst", O_RDONLY, &fh);
13460       hs_open_file(lt, "idf", O_RDONLY, &fhd);
13461       if (fh < 0 || fhd < 0) {
13462          cm_msg(MERROR, "hs_count_events", "cannot open index files");
13463          return HS_FILE_ERROR;
13464       }
13465 
13466       /* loop over index file */
13467       *id = 0;
13468       do {
13469          /* read definition index record */
13470          i = read(fhd, (char *) &def_rec, sizeof(def_rec));
13471          if (i < (int) sizeof(def_rec))
13472             break;
13473 
13474          if (strcmp(name, def_rec.event_name) == 0) {
13475             *id = def_rec.event_id;
13476             close(fh);
13477             close(fhd);
13478             return HS_SUCCESS;
13479          }
13480       } while (TRUE);
13481 
13482       close(fh);
13483       close(fhd);
13484 
13485       /* not found -> go back one day */
13486       lt -= 3600 * 24;
13487 
13488    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
13489 
13490    return HS_UNDEFINED_EVENT;
13491 }
13492 
13493 
13494 /********************************************************************/
13495 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
13496 /********************************************************************\
13497 
13498   Routine: hs_count_vars
13499 
13500   Purpose: Count number of variables for a given date and event id
13501 
13502   Input:
13503     DWORD  ltime            Date at which tags should be counted
13504 
13505   Output:
13506     DWORD  *count           Number of tags
13507 
13508   Function value:
13509     HS_SUCCESS              Successful completion
13510     HS_FILE_ERROR           Cannot open history file
13511 
13512 \********************************************************************/
13513 {
13514    int fh, fhd;
13515    INT i, n, status;
13516    DEF_RECORD def_rec;
13517    HIST_RECORD rec;
13518 
13519    if (rpc_is_remote())
13520       return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
13521 
13522    /* search latest history file */
13523    status = hs_search_file(&ltime, -1);
13524    if (status != HS_SUCCESS) {
13525       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
13526       return HS_FILE_ERROR;
13527    }
13528 
13529    /* open history and definition files */
13530    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13531    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13532    if (fh < 0 || fhd < 0) {
13533       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
13534       return HS_FILE_ERROR;
13535    }
13536 
13537    /* search last definition */
13538    lseek(fhd, 0, SEEK_END);
13539    n = TELL(fhd) / sizeof(def_rec);
13540    def_rec.event_id = 0;
13541    for (i = n - 1; i >= 0; i--) {
13542       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13543       read(fhd, (char *) &def_rec, sizeof(def_rec));
13544       if (def_rec.event_id == event_id)
13545          break;
13546    }
13547    if (def_rec.event_id != event_id) {
13548       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
13549       return HS_FILE_ERROR;
13550    }
13551 
13552    /* read definition */
13553    lseek(fh, def_rec.def_offset, SEEK_SET);
13554    read(fh, (char *) &rec, sizeof(rec));
13555    *count = rec.data_size / sizeof(TAG);
13556 
13557    close(fh);
13558    close(fhd);
13559 
13560    return HS_SUCCESS;
13561 }
13562 
13563 
13564 /********************************************************************/
13565 INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD * size,
13566                  DWORD * var_n, DWORD * n_size)
13567 /********************************************************************\
13568 
13569   Routine: hs_enum_vars
13570 
13571   Purpose: Enumerate variable tags for a given date and event id
13572 
13573   Input:
13574     DWORD  ltime            Date at which tags should be enumerated
13575     DWORD  event_id         Event ID
13576 
13577   Output:
13578     char   *var_name        Array containing variable names
13579     DWORD  *size            Size of name array
13580     DWORD  *var_n           Array size of variable
13581     DWORD  *n_size          Size of n array
13582 
13583   Function value:
13584     HS_SUCCESS              Successful completion
13585     HS_NO_MEMEORY           Out of memory
13586     HS_FILE_ERROR           Cannot open history file
13587 
13588 \********************************************************************/
13589 {
13590    char str[256];
13591    int fh, fhd;
13592    INT i, n, status;
13593    DEF_RECORD def_rec;
13594    HIST_RECORD rec;
13595    TAG *tag;
13596 
13597    if (rpc_is_remote())
13598       return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
13599 
13600    /* search latest history file */
13601    status = hs_search_file(&ltime, -1);
13602    if (status != HS_SUCCESS) {
13603       cm_msg(MERROR, "hs_enum_vars", "cannot find recent history file");
13604       return HS_FILE_ERROR;
13605    }
13606 
13607    /* open history and definition files */
13608    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13609    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13610    if (fh < 0 || fhd < 0) {
13611       cm_msg(MERROR, "hs_enum_vars", "cannot open index files");
13612       return HS_FILE_ERROR;
13613    }
13614 
13615    /* search last definition */
13616    lseek(fhd, 0, SEEK_END);
13617    n = TELL(fhd) / sizeof(def_rec);
13618    def_rec.event_id = 0;
13619    for (i = n - 1; i >= 0; i--) {
13620       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13621       read(fhd, (char *) &def_rec, sizeof(def_rec));
13622       if (def_rec.event_id == event_id)
13623          break;
13624    }
13625    if (def_rec.event_id != event_id) {
13626       cm_msg(MERROR, "hs_enum_vars", "event %d not found in index file", event_id);
13627       return HS_FILE_ERROR;
13628    }
13629 
13630    /* read definition header */
13631    lseek(fh, def_rec.def_offset, SEEK_SET);
13632    read(fh, (char *) &rec, sizeof(rec));
13633    read(fh, str, NAME_LENGTH);
13634 
13635    /* read event definition */
13636    n = rec.data_size / sizeof(TAG);
13637    tag = (TAG *) M_MALLOC(rec.data_size);
13638    read(fh, (char *) tag, rec.data_size);
13639 
13640    if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
13641 
13642       /* store partial definition */
13643       for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
13644          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13645          var_n[i] = tag[i].n_data;
13646       }
13647 
13648       cm_msg(MERROR, "hs_enum_vars", "tag buffer too small");
13649       M_FREE(tag);
13650       close(fh);
13651       close(fhd);
13652       return HS_NO_MEMORY;
13653    }
13654 
13655    /* store full definition */
13656    for (i = 0; i < n; i++) {
13657       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13658       var_n[i] = tag[i].n_data;
13659    }
13660    *size = n * NAME_LENGTH;
13661    *n_size = n * sizeof(DWORD);
13662 
13663    M_FREE(tag);
13664    close(fh);
13665    close(fhd);
13666 
13667    return HS_SUCCESS;
13668 }
13669 
13670 
13671 /********************************************************************/
13672 INT hs_get_var(DWORD ltime, DWORD event_id, char *var_name, DWORD * type, INT * n_data)
13673 /********************************************************************\
13674 
13675   Routine: hs_get_var
13676 
13677   Purpose: Get definition for certain variable
13678 
13679   Input:
13680     DWORD  ltime            Date at which variable definition should
13681                             be returned
13682     DWORD  event_id         Event ID
13683     char   *var_name        Name of variable
13684 
13685   Output:
13686     INT    *type            Type of variable
13687     INT    *n_data          Number of items in variable
13688 
13689   Function value:
13690     HS_SUCCESS              Successful completion
13691     HS_NO_MEMEORY           Out of memory
13692     HS_FILE_ERROR           Cannot open history file
13693 
13694 \********************************************************************/
13695 {
13696    char str[256];
13697    int fh, fhd;
13698    INT i, n, status;
13699    DEF_RECORD def_rec;
13700    HIST_RECORD rec;
13701    TAG *tag;
13702 
13703    if (rpc_is_remote())
13704       return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name, type, n_data);
13705 
13706    /* search latest history file */
13707    status = hs_search_file(&ltime, -1);
13708    if (status != HS_SUCCESS) {
13709       cm_msg(MERROR, "hs_get_var", "cannot find recent history file");
13710       return HS_FILE_ERROR;
13711    }
13712 
13713    /* open history and definition files */
13714    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13715    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13716    if (fh < 0 || fhd < 0) {
13717       cm_msg(MERROR, "hs_get_var", "cannot open index files");
13718       return HS_FILE_ERROR;
13719    }
13720 
13721    /* search last definition */
13722    lseek(fhd, 0, SEEK_END);
13723    n = TELL(fhd) / sizeof(def_rec);
13724    def_rec.event_id = 0;
13725    for (i = n - 1; i >= 0; i--) {
13726       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13727       read(fhd, (char *) &def_rec, sizeof(def_rec));
13728       if (def_rec.event_id == event_id)
13729          break;
13730    }
13731    if (def_rec.event_id != event_id) {
13732       cm_msg(MERROR, "hs_get_var", "event %d not found in index file", event_id);
13733       return HS_FILE_ERROR;
13734    }
13735 
13736    /* read definition header */
13737    lseek(fh, def_rec.def_offset, SEEK_SET);
13738    read(fh, (char *) &rec, sizeof(rec));
13739    read(fh, str, NAME_LENGTH);
13740 
13741    /* read event definition */
13742    n = rec.data_size / sizeof(TAG);
13743    tag = (TAG *) M_MALLOC(rec.data_size);
13744    read(fh, (char *) tag, rec.data_size);
13745 
13746    /* search variable */
13747    for (i = 0; i < n; i++)
13748       if (strcmp(tag[i].name, var_name) == 0)
13749          break;
13750 
13751    close(fh);
13752    close(fhd);
13753 
13754    if (i < n) {
13755       *type = tag[i].type;
13756       *n_data = tag[i].n_data;
13757    } else {
13758       *type = *n_data = 0;
13759       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
13760       M_FREE(tag);
13761       return HS_UNDEFINED_VAR;
13762    }
13763 
13764    M_FREE(tag);
13765    return HS_SUCCESS;
13766 }
13767 
13768 
13769 /********************************************************************/
13770 INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time,
13771             DWORD interval, char *tag_name, DWORD var_index,
13772             DWORD * time_buffer, DWORD * tbsize,
13773             void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
13774 /********************************************************************\
13775 
13776   Routine: hs_read
13777 
13778   Purpose: Read history for a variable at a certain time interval
13779 
13780   Input:
13781     DWORD  event_id         Event ID
13782     DWORD  start_time       Starting Date/Time
13783     DWORD  end_time         End Date/Time
13784     DWORD  interval         Minimum time in seconds between reported
13785                             events. Can be used to skip events
13786     char   *tag_name        Variable name inside event
13787     DWORD  var_index        Index if variable is array
13788 
13789   Output:
13790     DWORD  *time_buffer     Buffer containing times for each value
13791     DWORD  *tbsize          Size of time buffer
13792     void   *data_buffer     Buffer containing variable values
13793     DWORD  *dbsize          Data buffer size
13794     DWORD  *type            Type of variable (one of TID_xxx)
13795     DWORD  *n               Number of time/value pairs found
13796                             in specified interval and placed into
13797                             time_buffer and data_buffer
13798 
13799 
13800   Function value:
13801     HS_SUCCESS              Successful completion
13802     HS_NO_MEMEORY           Out of memory
13803     HS_FILE_ERROR           Cannot open history file
13804     HS_WRONG_INDEX          var_index exceeds array size of variable
13805     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
13806     HS_TRUNCATED            Buffer too small, data has been truncated
13807 
13808 \********************************************************************/
13809 {
13810    DWORD prev_time, last_irec_time;
13811    int fh, fhd, fhi, cp = 0;
13812    INT i, delta, index = 0, status, cache_size;
13813    INDEX_RECORD irec, *pirec;
13814    HIST_RECORD rec, drec;
13815    INT old_def_offset, var_size = 0, var_offset = 0;
13816    TAG *tag;
13817    char str[NAME_LENGTH];
13818    struct tm *tms;
13819    char *cache;
13820 
13821    if (rpc_is_remote())
13822       return rpc_call(RPC_HS_READ, event_id, start_time, end_time, interval,
13823                       tag_name, var_index, time_buffer, tbsize, data_buffer,
13824                       dbsize, type, n);
13825 
13826    /* if not time given, use present to one hour in past */
13827    if (start_time == 0)
13828       start_time = time(NULL) - 3600;
13829    if (end_time == 0)
13830       end_time = time(NULL);
13831 
13832    /* search history file for start_time */
13833    status = hs_search_file(&start_time, 1);
13834    if (status != HS_SUCCESS) {
13835       cm_msg(MERROR, "hs_read", "cannot find recent history file");
13836       *tbsize = *dbsize = *n = 0;
13837       return HS_FILE_ERROR;
13838    }
13839 
13840    /* open history and definition files */
13841    hs_open_file(start_time, "hst", O_RDONLY, &fh);
13842    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
13843    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
13844    if (fh < 0 || fhd < 0 || fhi < 0) {
13845       cm_msg(MERROR, "hs_read", "cannot open index files");
13846       *tbsize = *dbsize = *n = 0;
13847       return HS_FILE_ERROR;
13848    }
13849 
13850    /* try to read index file into cache */
13851    lseek(fhi, 0, SEEK_END);
13852    cache_size = TELL(fhi);
13853 
13854    if (cache_size > 0) {
13855       cache = (char *) M_MALLOC(cache_size);
13856       if (cache) {
13857          lseek(fhi, 0, SEEK_SET);
13858          i = read(fhi, cache, cache_size);
13859          if (i < cache_size) {
13860             M_FREE(cache);
13861             close(fh);
13862             close(fhd);
13863             close(fhi);
13864             return HS_FILE_ERROR;
13865          }
13866       }
13867 
13868       /* search record closest to start time */
13869       if (cache == NULL) {
13870          lseek(fhi, 0, SEEK_END);
13871          delta = (TELL(fhi) / sizeof(irec)) / 2;
13872          lseek(fhi, delta * sizeof(irec), SEEK_SET);
13873          do {
13874             delta = (int) (abs(delta) / 2.0 + 0.5);
13875             read(fhi, (char *) &irec, sizeof(irec));
13876             if (irec.time > start_time)
13877                delta = -delta;
13878 
13879             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
13880          } while (abs(delta) > 1 && irec.time != start_time);
13881          read(fhi, (char *) &irec, sizeof(irec));
13882          if (irec.time > start_time)
13883             delta = -abs(delta);
13884 
13885          i = TELL(fhi) + (delta - 1) * sizeof(irec);
13886          if (i <= 0)
13887             lseek(fhi, 0, SEEK_SET);
13888          else
13889             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
13890          read(fhi, (char *) &irec, sizeof(irec));
13891       } else {
13892          delta = (cache_size / sizeof(irec)) / 2;
13893          cp = delta * sizeof(irec);
13894          do {
13895             delta = (int) (abs(delta) / 2.0 + 0.5);
13896             pirec = (INDEX_RECORD *) (cache + cp);
13897             if (pirec->time > start_time)
13898                delta = -delta;
13899 
13900             cp = cp + delta * sizeof(irec);
13901          } while (abs(delta) > 1 && pirec->time != start_time);
13902          pirec = (INDEX_RECORD *) (cache + cp);
13903          if (pirec->time > start_time)
13904             delta = -abs(delta);
13905 
13906          if (cp <= delta * (int) sizeof(irec))
13907             cp = 0;
13908          else
13909             cp = cp + delta * sizeof(irec);
13910 
13911          if (cp >= cache_size)
13912             cp = cache_size - sizeof(irec);
13913          if (cp < 0)
13914             cp = 0;
13915 
13916          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
13917          cp += sizeof(irec);
13918       }
13919    } else {                     /* file size > 0 */
13920 
13921       cache = NULL;
13922       irec.time = start_time;
13923    }
13924 
13925    /* read records, skip wrong IDs */
13926    old_def_offset = -1;
13927    *n = 0;
13928    prev_time = 0;
13929    last_irec_time = 0;
13930    do {
13931       if (irec.time < last_irec_time) {
13932          cm_msg(MERROR, "hs_read",
13933                 "corrupted history data: time does not increase: %d -> %d",
13934                 last_irec_time, irec.time);
13935          *tbsize = *dbsize = *n = 0;
13936          return HS_FILE_ERROR;
13937       }
13938       last_irec_time = irec.time;
13939       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
13940          /* check if record time more than "interval" seconds after previous time */
13941          if (irec.time >= prev_time + interval) {
13942             prev_time = irec.time;
13943             lseek(fh, irec.offset, SEEK_SET);
13944             read(fh, (char *) &rec, sizeof(rec));
13945 
13946             /* if definition changed, read new definition */
13947             if ((INT) rec.def_offset != old_def_offset) {
13948                lseek(fh, rec.def_offset, SEEK_SET);
13949                read(fh, (char *) &drec, sizeof(drec));
13950                read(fh, str, NAME_LENGTH);
13951 
13952                tag = (TAG *) M_MALLOC(drec.data_size);
13953                if (tag == NULL) {
13954                   *n = *tbsize = *dbsize = 0;
13955                   if (cache)
13956                      M_FREE(cache);
13957                   close(fh);
13958                   close(fhd);
13959                   close(fhi);
13960                   return HS_NO_MEMORY;
13961                }
13962                read(fh, (char *) tag, drec.data_size);
13963 
13964                /* find index of tag_name in new definition */
13965                index = -1;
13966                for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
13967                   if (equal_ustring(tag[i].name, tag_name)) {
13968                      index = i;
13969                      break;
13970                   }
13971 
13972                /*
13973                   if ((DWORD) i == drec.data_size/sizeof(TAG))
13974                   {
13975                   *n = *tbsize = *dbsize = 0;
13976                   if (cache)
13977                   M_FREE(cache);
13978 
13979                   return HS_UNDEFINED_VAR;
13980                   }
13981                 */
13982 
13983                if (index >= 0 && var_index >= tag[i].n_data) {
13984                   *n = *tbsize = *dbsize = 0;
13985                   if (cache)
13986                      M_FREE(cache);
13987                   M_FREE(tag);
13988                   close(fh);
13989                   close(fhd);
13990                   close(fhi);
13991                   return HS_WRONG_INDEX;
13992                }
13993 
13994                /* calculate offset for variable */
13995                if (index >= 0) {
13996                   *type = tag[i].type;
13997 
13998                   /* loop over all previous variables */
13999                   for (i = 0, var_offset = 0; i < index; i++)
14000                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
14001 
14002                   /* strings have size n_data */
14003                   if (tag[index].type == TID_STRING)
14004                      var_size = tag[i].n_data;
14005                   else
14006                      var_size = rpc_tid_size(tag[index].type);
14007 
14008                   var_offset += var_size * var_index;
14009                }
14010 
14011                M_FREE(tag);
14012                old_def_offset = rec.def_offset;
14013                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14014             }
14015 
14016             if (index >= 0) {
14017                /* check if data fits in buffers */
14018                if ((*n) * sizeof(DWORD) >= *tbsize || (*n) * var_size >= *dbsize) {
14019                   *dbsize = (*n) * var_size;
14020                   *tbsize = (*n) * sizeof(DWORD);
14021                   if (cache)
14022                      M_FREE(cache);
14023                   close(fh);
14024                   close(fhd);
14025                   close(fhi);
14026                   return HS_TRUNCATED;
14027                }
14028 
14029                /* copy time from header */
14030                time_buffer[*n] = irec.time;
14031 
14032                /* copy data from record */
14033                lseek(fh, var_offset, SEEK_CUR);
14034                read(fh, (char *) data_buffer + (*n) * var_size, var_size);
14035 
14036                /* increment counter */
14037                (*n)++;
14038             }
14039          }
14040       }
14041 
14042       /* read next index record */
14043       if (cache) {
14044          if (cp >= cache_size) {
14045             i = -1;
14046             M_FREE(cache);
14047             cache = NULL;
14048          } else
14049             i = sizeof(irec);
14050 
14051          if (cp < cache_size) {
14052             memcpy(&irec, cache + cp, sizeof(irec));
14053             cp += sizeof(irec);
14054          }
14055       } else
14056          i = read(fhi, (char *) &irec, sizeof(irec));
14057 
14058       /* end of file: search next history file */
14059       if (i <= 0) {
14060          close(fh);
14061          close(fhd);
14062          close(fhi);
14063 
14064          /* advance one day */
14065          tms = localtime((const time_t *)(POINTER_T)&last_irec_time);
14066          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14067          last_irec_time = mktime(tms);
14068 
14069          last_irec_time += 3600 * 24;
14070 
14071          if (last_irec_time > end_time)
14072             break;
14073 
14074          /* search next file */
14075          status = hs_search_file(&last_irec_time, 1);
14076          if (status != HS_SUCCESS)
14077             break;
14078 
14079          /* open history and definition files */
14080          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14081          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14082          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14083          if (fh < 0 || fhd < 0 || fhi < 0) {
14084             cm_msg(MERROR, "hs_read", "cannot open index files");
14085             break;
14086          }
14087 
14088          /* try to read index file into cache */
14089          lseek(fhi, 0, SEEK_END);
14090          cache_size = TELL(fhi);
14091          lseek(fhi, 0, SEEK_SET);
14092          cache = (char *) M_MALLOC(cache_size);
14093          if (cache) {
14094             i = read(fhi, cache, cache_size);
14095             if (i < cache_size)
14096                break;
14097             /* read first record */
14098             cp = 0;
14099             memcpy(&irec, cache, sizeof(irec));
14100          } else {
14101             /* read first record */
14102             i = read(fhi, (char *) &irec, sizeof(irec));
14103             if (i <= 0)
14104                break;
14105          }
14106 
14107          /* old definition becomes invalid */
14108          old_def_offset = -1;
14109       }
14110    } while (irec.time < end_time);
14111 
14112    if (cache)
14113       M_FREE(cache);
14114    close(fh);
14115    close(fhd);
14116    close(fhi);
14117 
14118    *dbsize = *n * var_size;
14119    *tbsize = *n * sizeof(DWORD);
14120 
14121    return HS_SUCCESS;
14122 }
14123 
14124 
14125 /********************************************************************/
14126 INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time,
14127             DWORD interval, BOOL binary_time)
14128 /********************************************************************\
14129 
14130   Routine: hs_dump
14131 
14132   Purpose: Display history for a given event at stdout. The output
14133            can be redirected to be read by Excel for example.
14134 
14135   Input:
14136     DWORD  event_id         Event ID
14137     DWORD  start_time       Starting Date/Time
14138     DWORD  end_time         End Date/Time
14139     DWORD  interval         Minimum time in seconds between reported
14140                             events. Can be used to skip events
14141     BOOL   binary_time      Display DWORD time stamp
14142   Output:
14143     <screen output>
14144 
14145   Function value:
14146     HS_SUCCESS              Successful completion
14147     HS_FILE_ERROR           Cannot open history file
14148 
14149 \********************************************************************/
14150 {
14151    DWORD prev_time, last_irec_time;
14152    int fh, fhd, fhi;
14153    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
14154    INDEX_RECORD irec;
14155    HIST_RECORD rec, drec;
14156    INT old_def_offset, offset;
14157    TAG *tag = NULL, *old_tag = NULL;
14158    char str[NAME_LENGTH], data_buffer[10000];
14159    struct tm *tms;
14160 
14161    /* if not time given, use present to one hour in past */
14162    if (start_time == 0)
14163       start_time = time(NULL) - 3600;
14164    if (end_time == 0)
14165       end_time = time(NULL);
14166 
14167    /* search history file for start_time */
14168    status = hs_search_file(&start_time, 1);
14169    if (status != HS_SUCCESS) {
14170       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
14171       return HS_FILE_ERROR;
14172    }
14173 
14174    /* open history and definition files */
14175    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14176    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14177    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14178    if (fh < 0 || fhd < 0 || fhi < 0) {
14179       cm_msg(MERROR, "hs_dump", "cannot open index files");
14180       return HS_FILE_ERROR;
14181    }
14182 
14183    /* search record closest to start time */
14184    lseek(fhi, 0, SEEK_END);
14185    delta = (TELL(fhi) / sizeof(irec)) / 2;
14186    lseek(fhi, delta * sizeof(irec), SEEK_SET);
14187    do {
14188       delta = (int) (abs(delta) / 2.0 + 0.5);
14189       read(fhi, (char *) &irec, sizeof(irec));
14190       if (irec.time > start_time)
14191          delta = -delta;
14192 
14193       i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14194    } while (abs(delta) > 1 && irec.time != start_time);
14195    read(fhi, (char *) &irec, sizeof(irec));
14196    if (irec.time > start_time)
14197       delta = -abs(delta);
14198 
14199    i = TELL(fhi) + (delta - 1) * sizeof(irec);
14200    if (i <= 0)
14201       lseek(fhi, 0, SEEK_SET);
14202    else
14203       lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14204    read(fhi, (char *) &irec, sizeof(irec));
14205 
14206    /* read records, skip wrong IDs */
14207    old_def_offset = -1;
14208    prev_time = 0;
14209    last_irec_time = 0;
14210    do {
14211       if (irec.time < last_irec_time) {
14212          cm_msg(MERROR, "hs_dump",
14213                 "corrupted history data: time does not increase: %d -> %d",
14214                 last_irec_time, irec.time);
14215          return HS_FILE_ERROR;
14216       }
14217       last_irec_time = irec.time;
14218       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14219          if (irec.time >= prev_time + interval) {
14220             prev_time = irec.time;
14221             lseek(fh, irec.offset, SEEK_SET);
14222             read(fh, (char *) &rec, sizeof(rec));
14223 
14224             /* if definition changed, read new definition */
14225             if ((INT) rec.def_offset != old_def_offset) {
14226                lseek(fh, rec.def_offset, SEEK_SET);
14227                read(fh, (char *) &drec, sizeof(drec));
14228                read(fh, str, NAME_LENGTH);
14229 
14230                if (tag == NULL)
14231                   tag = (TAG *) M_MALLOC(drec.data_size);
14232                else
14233                   tag = (TAG *) realloc(tag, drec.data_size);
14234                if (tag == NULL)
14235                   return HS_NO_MEMORY;
14236                read(fh, (char *) tag, drec.data_size);
14237                n_tag = drec.data_size / sizeof(TAG);
14238 
14239                /* print tag names if definition has changed */
14240                if (old_tag == NULL || old_n_tag != n_tag ||
14241                    memcmp(old_tag, tag, drec.data_size) != 0) {
14242                   printf("Date\t");
14243                   for (i = 0; i < n_tag; i++) {
14244                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
14245                         printf("%s\t", tag[i].name);
14246                      else
14247                         for (j = 0; j < (INT) tag[i].n_data; j++)
14248                            printf("%s%d\t", tag[i].name, j);
14249                   }
14250                   printf("\n");
14251 
14252                   if (old_tag == NULL)
14253                      old_tag = (TAG *) M_MALLOC(drec.data_size);
14254                   else
14255                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
14256                   memcpy(old_tag, tag, drec.data_size);
14257                   old_n_tag = n_tag;
14258                }
14259 
14260                old_def_offset = rec.def_offset;
14261                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14262             }
14263 
14264             /* print time from header */
14265             if (binary_time)
14266                printf("%d ", irec.time);
14267             else {
14268                sprintf(str, "%s", ctime((const time_t *) &irec.time) + 4);
14269                str[20] = '\t';
14270                printf(str);
14271             }
14272 
14273             /* read data */
14274             read(fh, data_buffer, rec.data_size);
14275 
14276             /* interprete data from tag definition */
14277             offset = 0;
14278             for (i = 0; i < n_tag; i++) {
14279                /* strings have a length of n_data */
14280                if (tag[i].type == TID_STRING) {
14281                   printf("%s\t", data_buffer + offset);
14282                   offset += tag[i].n_data;
14283                } else if (tag[i].n_data == 1) {
14284                   /* non-array data */
14285                   db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14286                              tag[i].type);
14287                   printf("%s\t", str);
14288                   offset += rpc_tid_size(tag[i].type);
14289                } else
14290                   /* loop over array data */
14291                   for (j = 0; j < (INT) tag[i].n_data; j++) {
14292                      db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14293                                 tag[i].type);
14294                      printf("%s\t", str);
14295                      offset += rpc_tid_size(tag[i].type);
14296                   }
14297             }
14298             printf("\n");
14299          }
14300       }
14301 
14302       /* read next index record */
14303       i = read(fhi, (char *) &irec, sizeof(irec));
14304 
14305       /* end of file: search next history file */
14306       if (i <= 0) {
14307          close(fh);
14308          close(fhd);
14309          close(fhi);
14310 
14311          /* advance one day */
14312          tms = localtime((const time_t *)(POINTER_T)&last_irec_time);
14313          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14314          last_irec_time = mktime(tms);
14315 
14316          last_irec_time += 3600 * 24;
14317          if (last_irec_time > end_time)
14318             break;
14319 
14320          /* search next file */
14321          status = hs_search_file((DWORD *) &last_irec_time, 1);
14322          if (status != HS_SUCCESS)
14323             break;
14324 
14325          /* open history and definition files */
14326          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14327          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14328          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14329          if (fh < 0 || fhd < 0 || fhi < 0) {
14330             cm_msg(MERROR, "hs_dump", "cannot open index files");
14331             break;
14332          }
14333 
14334          /* read first record */
14335          i = read(fhi, (char *) &irec, sizeof(irec));
14336          if (i <= 0)
14337             break;
14338 
14339          /* old definition becomes invalid */
14340          old_def_offset = -1;
14341       }
14342    } while (irec.time < end_time);
14343 
14344    M_FREE(tag);
14345    M_FREE(old_tag);
14346    close(fh);
14347    close(fhd);
14348    close(fhi);
14349 
14350    return HS_SUCCESS;
14351 }
14352 
14353 
14354 /********************************************************************/
14355 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
14356 /********************************************************************\
14357 
14358   Routine: hs_fdump
14359 
14360   Purpose: Display history for a given history file
14361 
14362   Input:
14363     char   *file_name       Name of file to dump
14364     DWORD  event_id         Event ID
14365     BOOL   binary_time      Display DWORD time stamp
14366 
14367   Output:
14368     <screen output>
14369 
14370   Function value:
14371     HS_SUCCESS              Successful completion
14372     HS_FILE_ERROR           Cannot open history file
14373 
14374 \********************************************************************/
14375 {
14376    int fh;
14377    INT n;
14378    HIST_RECORD rec;
14379    char event_name[NAME_LENGTH];
14380    char str[80];
14381 
14382    /* open file, add O_BINARY flag for Windows NT */
14383    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
14384    if (fh < 0) {
14385       cm_msg(MERROR, "hs_fdump", "cannot open file %s", file_name);
14386       return HS_FILE_ERROR;
14387    }
14388 
14389    /* loop over file records in .hst file */
14390    do {
14391       n = read(fh, (char *) &rec, sizeof(rec));
14392       if (n < sizeof(rec))
14393          break;
14394 
14395       /* check if record type is definition */
14396       if (rec.record_type == RT_DEF) {
14397          /* read name */
14398          read(fh, event_name, sizeof(event_name));
14399 
14400          if (rec.event_id == id || id == 0)
14401             printf("Event definition %s, ID %d\n", event_name, rec.event_id);
14402 
14403          /* skip tags */
14404          lseek(fh, rec.data_size, SEEK_CUR);
14405       } else {
14406          /* print data record */
14407          if (binary_time)
14408             sprintf(str, "%d ", rec.time);
14409          else {
14410             strcpy(str, ctime((const time_t *) &rec.time) + 4);
14411             str[15] = 0;
14412          }
14413          if (rec.event_id == id || id == 0)
14414             printf("ID %d, %s, size %d\n", rec.event_id, str, rec.data_size);
14415 
14416          /* skip data */
14417          lseek(fh, rec.data_size, SEEK_CUR);
14418       }
14419 
14420    } while (TRUE);
14421 
14422    close(fh);
14423 
14424    return HS_SUCCESS;
14425 }
14426 #endif                          /* OS_VXWORKS hs section */
14427 
14428 /**dox***************************************************************/
14429 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14430 
14431 /**dox***************************************************************/
14432                             /** @} *//* end of hsfunctionc */
14433 
14434 /**dox***************************************************************/
14435 /** @addtogroup elfunctionc
14436  *  
14437  *  @{  */
14438 
14439 /**dox***************************************************************/
14440 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14441 
14442 /********************************************************************\
14443 *                                                                    *
14444 *               Electronic logbook functions                         *
14445 *                                                                    *
14446 \********************************************************************/
14447 
14448 /********************************************************************/
14449 void el_decode(char *message, char *key, char *result, int size)
14450 {
14451    char *rstart = result;
14452    char *pc;
14453 
14454    if (result == NULL)
14455       return;
14456 
14457    *result = 0;
14458 
14459    if (strstr(message, key)) {
14460       for (pc = strstr(message, key) + strlen(key); *pc != '\n';)
14461          *result++ = *pc++;
14462       *result = 0;
14463    }
14464 
14465    assert((int) strlen(rstart) < size);
14466 }
14467 
14468 /**dox***************************************************************/
14469 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14470 
14471 /********************************************************************/
14472 /**
14473 Submit an ELog entry.
14474 @param run  Run Number.
14475 @param author Message author.
14476 @param type Message type.
14477 @param system Message system.
14478 @param subject Subject.
14479 @param text Message text.
14480 @param reply_to In reply to this message.
14481 @param encoding Text encoding, either HTML or plain.
14482 @param afilename1   File name of attachment.
14483 @param buffer1      File contents.
14484 @param buffer_size1 Size of buffer in bytes.
14485 @param afilename2   File name of attachment.
14486 @param buffer2      File contents.
14487 @param buffer_size2 Size of buffer in bytes.
14488 @param afilename3   File name of attachment.
14489 @param buffer3      File contents.
14490 @param buffer_size3 Size of buffer in bytes.
14491 @param tag          If given, edit existing message.
14492 @param tag_size     Maximum size of tag.
14493 @return EL_SUCCESS
14494 */
14495 INT el_submit(int run, char *author, char *type, char *system, char *subject,
14496               char *text, char *reply_to, char *encoding,
14497               char *afilename1, char *buffer1, INT buffer_size1,
14498               char *afilename2, char *buffer2, INT buffer_size2,
14499               char *afilename3, char *buffer3, INT buffer_size3, char *tag, INT tag_size)
14500 {
14501    if (rpc_is_remote())
14502       return rpc_call(RPC_EL_SUBMIT, run, author, type, system, subject,
14503                       text, reply_to, encoding,
14504                       afilename1, buffer1, buffer_size1,
14505                       afilename2, buffer2, buffer_size2,
14506                       afilename3, buffer3, buffer_size3, tag, tag_size);
14507 
14508 #ifdef LOCAL_ROUTINES
14509    {
14510       INT n, size, fh, status, run_number, mutex, buffer_size = 0, index, offset =
14511           0, tail_size = 0;
14512       struct tm *tms = NULL;
14513       char afilename[256], file_name[256], afile_name[3][256], dir[256], str[256],
14514           start_str[80], end_str[80], last[80], date[80], thread[80], attachment[256];
14515       HNDLE hDB;
14516       time_t now;
14517       char message[10000], *p, *buffer = NULL;
14518       BOOL bedit;
14519 
14520       cm_get_experiment_database(&hDB, NULL);
14521 
14522       bedit = (tag[0] != 0);
14523 
14524       /* request semaphore */
14525       cm_get_experiment_mutex(NULL, &mutex);
14526       status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
14527       if (status != SS_SUCCESS) {
14528          cm_msg(MERROR, "el_submit",
14529                 "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
14530          abort();
14531       }
14532 
14533       /* get run number from ODB if not given */
14534       if (run > 0)
14535          run_number = run;
14536       else {
14537          /* get run number */
14538          size = sizeof(run_number);
14539          status =
14540              db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT,
14541                           TRUE);
14542          assert(status == SUCCESS);
14543       }
14544 
14545       if (run_number < 0) {
14546          cm_msg(MERROR, "el_submit", "aborting on attempt to use invalid run number %d",
14547                 run_number);
14548          abort();
14549       }
14550 
14551       for (index = 0; index < 3; index++) {
14552          /* generate filename for attachment */
14553          afile_name[index][0] = file_name[0] = 0;
14554 
14555          if (index == 0) {
14556             strcpy(afilename, afilename1);
14557             buffer = buffer1;
14558             buffer_size = buffer_size1;
14559          } else if (index == 1) {
14560             strcpy(afilename, afilename2);
14561             buffer = buffer2;
14562             buffer_size = buffer_size2;
14563          } else if (index == 2) {
14564             strcpy(afilename, afilename3);
14565             buffer = buffer3;
14566             buffer_size = buffer_size3;
14567          }
14568 
14569          if (afilename[0]) {
14570             strcpy(file_name, afilename);
14571             p = file_name;
14572             while (strchr(p, ':'))
14573                p = strchr(p, ':') + 1;
14574             while (strchr(p, '\\'))
14575                p = strchr(p, '\\') + 1; /* NT */
14576             while (strchr(p, '/'))
14577                p = strchr(p, '/') + 1;  /* Unix */
14578             while (strchr(p, ']'))
14579                p = strchr(p, ']') + 1;  /* VMS */
14580 
14581             /* assemble ELog filename */
14582             if (p[0]) {
14583                dir[0] = 0;
14584                if (hDB > 0) {
14585                   size = sizeof(dir);
14586                   memset(dir, 0, size);
14587                   status =
14588                       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING,
14589                                    FALSE);
14590                   if (status != DB_SUCCESS)
14591                      db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING,
14592                                   TRUE);
14593 
14594                   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14595                      strcat(dir, DIR_SEPARATOR_STR);
14596                }
14597 #if !defined(OS_VXWORKS)
14598 #if !defined(OS_VMS)
14599                tzset();
14600 #endif
14601 #endif
14602 
14603                time((time_t *) & now);
14604                tms = localtime((const time_t *) &now);
14605 
14606                strcpy(str, p);
14607                sprintf(afile_name[index], "%02d%02d%02d_%02d%02d%02d_%s",
14608                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14609                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14610                sprintf(file_name, "%s%02d%02d%02d_%02d%02d%02d_%s", dir,
14611                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14612                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14613 
14614                /* save attachment */
14615                fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14616                if (fh < 0) {
14617                   cm_msg(MERROR, "el_submit", "Cannot write attachment file \"%s\"",
14618                          file_name);
14619                } else {
14620                   write(fh, buffer, buffer_size);
14621                   close(fh);
14622                }
14623             }
14624          }
14625       }
14626 
14627       /* generate new file name YYMMDD.log in data directory */
14628       cm_get_experiment_database(&hDB, NULL);
14629 
14630       size = sizeof(dir);
14631       memset(dir, 0, size);
14632       status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
14633       if (status != DB_SUCCESS)
14634          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
14635 
14636       if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14637          strcat(dir, DIR_SEPARATOR_STR);
14638 
14639 #if !defined(OS_VXWORKS)
14640 #if !defined(OS_VMS)
14641       tzset();
14642 #endif
14643 #endif
14644 
14645       if (bedit) {
14646          /* edit existing message */
14647          strcpy(str, tag);
14648          if (strchr(str, '.')) {
14649             offset = atoi(strchr(str, '.') + 1);
14650             *strchr(str, '.') = 0;
14651          }
14652          sprintf(file_name, "%s%s.log", dir, str);
14653          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14654          if (fh < 0) {
14655             ss_mutex_release(mutex);
14656             return EL_FILE_ERROR;
14657          }
14658          lseek(fh, offset, SEEK_SET);
14659          read(fh, str, 16);
14660          size = atoi(str + 9);
14661          read(fh, message, size);
14662 
14663          el_decode(message, "Date: ", date, sizeof(date));
14664          el_decode(message, "Thread: ", thread, sizeof(thread));
14665          el_decode(message, "Attachment: ", attachment, sizeof(attachment));
14666 
14667          /* buffer tail of logfile */
14668          lseek(fh, 0, SEEK_END);
14669          tail_size = TELL(fh) - (offset + size);
14670 
14671          if (tail_size > 0) {
14672             buffer = (char *) M_MALLOC(tail_size);
14673             if (buffer == NULL) {
14674                close(fh);
14675                ss_mutex_release(mutex);
14676                return EL_FILE_ERROR;
14677             }
14678 
14679             lseek(fh, offset + size, SEEK_SET);
14680             n = read(fh, buffer, tail_size);
14681          }
14682          lseek(fh, offset, SEEK_SET);
14683       } else {
14684          /* create new message */
14685          time((time_t *) & now);
14686          tms = localtime((const time_t *) &now);
14687 
14688          sprintf(file_name, "%s%02d%02d%02d.log", dir,
14689                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14690 
14691          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14692          if (fh < 0) {
14693             ss_mutex_release(mutex);
14694             return EL_FILE_ERROR;
14695          }
14696 
14697          strcpy(date, ctime(&now));
14698          date[24] = 0;
14699 
14700          if (reply_to[0])
14701             sprintf(thread, "%16s %16s", reply_to, "0");
14702          else
14703             sprintf(thread, "%16s %16s", "0", "0");
14704 
14705          lseek(fh, 0, SEEK_END);
14706       }
14707 
14708       /* compose message */
14709 
14710       sprintf(message, "Date: %s\n", date);
14711       sprintf(message + strlen(message), "Thread: %s\n", thread);
14712       sprintf(message + strlen(message), "Run: %d\n", run_number);
14713       sprintf(message + strlen(message), "Author: %s\n", author);
14714       sprintf(message + strlen(message), "Type: %s\n", type);
14715       sprintf(message + strlen(message), "System: %s\n", system);
14716       sprintf(message + strlen(message), "Subject: %s\n", subject);
14717 
14718       /* keep original attachment if edit and no new attachment */
14719       if (bedit && afile_name[0][0] == 0 && afile_name[1][0] == 0 &&
14720           afile_name[2][0] == 0)
14721          sprintf(message + strlen(message), "Attachment: %s", attachment);
14722       else {
14723          sprintf(message + strlen(message), "Attachment: %s", afile_name[0]);
14724          if (afile_name[1][0])
14725             sprintf(message + strlen(message), ",%s", afile_name[1]);
14726          if (afile_name[2][0])
14727             sprintf(message + strlen(message), ",%s", afile_name[2]);
14728       }
14729       sprintf(message + strlen(message), "\n");
14730 
14731       sprintf(message + strlen(message), "Encoding: %s\n", encoding);
14732       sprintf(message + strlen(message), "========================================\n");
14733       strcat(message, text);
14734 
14735       assert(strlen(message) < sizeof(message));        /* bomb out on array overrun. */
14736 
14737       size = 0;
14738       sprintf(start_str, "$Start$: %6d\n", size);
14739       sprintf(end_str, "$End$:   %6d\n\f", size);
14740 
14741       size = strlen(message) + strlen(start_str) + strlen(end_str);
14742 
14743       if (tag != NULL && !bedit)
14744          sprintf(tag, "%02d%02d%02d.%d", tms->tm_year % 100, tms->tm_mon + 1,
14745                  tms->tm_mday, (int) TELL(fh));
14746 
14747       /* size has to fit in 6 digits */
14748       assert(size < 999999);
14749 
14750       sprintf(start_str, "$Start$: %6d\n", size);
14751       sprintf(end_str, "$End$:   %6d\n\f", size);
14752 
14753       write(fh, start_str, strlen(start_str));
14754       write(fh, message, strlen(message));
14755       write(fh, end_str, strlen(end_str));
14756 
14757       if (bedit) {
14758          if (tail_size > 0) {
14759             n = write(fh, buffer, tail_size);
14760             M_FREE(buffer);
14761          }
14762 
14763          /* truncate file here */
14764 #ifdef OS_WINNT
14765          chsize(fh, TELL(fh));
14766 #else
14767          ftruncate(fh, TELL(fh));
14768 #endif
14769       }
14770 
14771       close(fh);
14772 
14773       /* if reply, mark original message */
14774       if (reply_to[0] && !bedit) {
14775          strcpy(last, reply_to);
14776          do {
14777             status = el_search_message(last, &fh, FALSE);
14778             if (status == EL_SUCCESS) {
14779                /* position to next thread location */
14780                lseek(fh, 72, SEEK_CUR);
14781                memset(str, 0, sizeof(str));
14782                read(fh, str, 16);
14783                lseek(fh, -16, SEEK_CUR);
14784 
14785                /* if no reply yet, set it */
14786                if (atoi(str) == 0) {
14787                   sprintf(str, "%16s", tag);
14788                   write(fh, str, 16);
14789                   close(fh);
14790                   break;
14791                } else {
14792                   /* if reply set, find last one in chain */
14793                   strcpy(last, strtok(str, " "));
14794                   close(fh);
14795                }
14796             } else
14797                /* stop on error */
14798                break;
14799 
14800          } while (TRUE);
14801       }
14802 
14803       /* release elog mutex */
14804       ss_mutex_release(mutex);
14805    }
14806 #endif                          /* LOCAL_ROUTINES */
14807 
14808    return EL_SUCCESS;
14809 }
14810 
14811 /**dox***************************************************************/
14812 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14813 
14814 /********************************************************************/
14815 INT el_search_message(char *tag, int *fh, BOOL walk)
14816 {
14817    int i, size, offset, direction, last, status;
14818    struct tm *tms, ltms;
14819    time_t lt, ltime, lact;
14820    char str[256], file_name[256], dir[256];
14821    HNDLE hDB;
14822 
14823 #if !defined(OS_VXWORKS)
14824 #if !defined(OS_VMS)
14825    tzset();
14826 #endif
14827 #endif
14828 
14829    /* open file */
14830    cm_get_experiment_database(&hDB, NULL);
14831 
14832    size = sizeof(dir);
14833    memset(dir, 0, size);
14834    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
14835    if (status != DB_SUCCESS)
14836       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
14837 
14838    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14839       strcat(dir, DIR_SEPARATOR_STR);
14840 
14841    /* check tag for direction */
14842    direction = 0;
14843    if (strpbrk(tag, "+-")) {
14844       direction = atoi(strpbrk(tag, "+-"));
14845       *strpbrk(tag, "+-") = 0;
14846    }
14847 
14848    /* if tag is given, open file directly */
14849    if (tag[0]) {
14850       /* extract time structure from tag */
14851       tms = &ltms;
14852       memset(tms, 0, sizeof(struct tm));
14853       tms->tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
14854       tms->tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
14855       tms->tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
14856       tms->tm_hour = 12;
14857 
14858       if (tms->tm_year < 90)
14859          tms->tm_year += 100;
14860       ltime = lt = mktime(tms);
14861 
14862       strcpy(str, tag);
14863       if (strchr(str, '.')) {
14864          offset = atoi(strchr(str, '.') + 1);
14865          *strchr(str, '.') = 0;
14866       } else
14867          return EL_FILE_ERROR;
14868 
14869       do {
14870          tms = localtime((const time_t *) &ltime);
14871 
14872          sprintf(file_name, "%s%02d%02d%02d.log", dir,
14873                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14874          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
14875 
14876          if (*fh < 0) {
14877             if (!walk)
14878                return EL_FILE_ERROR;
14879 
14880             if (direction == -1)
14881                ltime -= 3600 * 24;      /* one day back */
14882             else
14883                ltime += 3600 * 24;      /* go forward one day */
14884 
14885             /* set new tag */
14886             tms = localtime((const time_t *) &ltime);
14887             sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1,
14888                     tms->tm_mday);
14889          }
14890 
14891          /* in forward direction, stop today */
14892          if (direction != -1 && ltime > time(NULL) + 3600 * 24)
14893             break;
14894 
14895          /* in backward direction, go back 10 years */
14896          if (direction == -1 && abs((INT) lt - (INT) ltime) > 3600 * 24 * 365 * 10)
14897             break;
14898 
14899       } while (*fh < 0);
14900 
14901       if (*fh < 0)
14902          return EL_FILE_ERROR;
14903 
14904       lseek(*fh, offset, SEEK_SET);
14905 
14906       /* check if start of message */
14907       i = read(*fh, str, 15);
14908       if (i <= 0) {
14909          close(*fh);
14910          return EL_FILE_ERROR;
14911       }
14912 
14913       if (strncmp(str, "$Start$: ", 9) != 0) {
14914          close(*fh);
14915          return EL_FILE_ERROR;
14916       }
14917 
14918       lseek(*fh, offset, SEEK_SET);
14919    }
14920 
14921    /* open most recent file if no tag given */
14922    if (tag[0] == 0) {
14923       time((long *) &lt);
14924       ltime = lt;
14925       do {
14926          tms = localtime((const time_t *) &ltime);
14927 
14928          sprintf(file_name, "%s%02d%02d%02d.log", dir,
14929                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14930          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
14931 
14932          if (*fh < 0)
14933             ltime -= 3600 * 24; /* one day back */
14934 
14935       } while (*fh < 0 && (INT) lt - (INT) ltime < 3600 * 24 * 365);
14936 
14937       if (*fh < 0)
14938          return EL_FILE_ERROR;
14939 
14940       /* remember tag */
14941       sprintf(tag, "%02d%02d%02d", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14942 
14943       lseek(*fh, 0, SEEK_END);
14944 
14945       sprintf(tag + strlen(tag), ".%d", (int) TELL(*fh));
14946    }
14947 
14948 
14949    if (direction == -1) {
14950       /* seek previous message */
14951 
14952       if (TELL(*fh) == 0) {
14953          /* go back one day */
14954          close(*fh);
14955 
14956          lt = ltime;
14957          do {
14958             lt -= 3600 * 24;
14959             tms = localtime((const time_t *) &lt);
14960             sprintf(str, "%02d%02d%02d.0",
14961                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14962 
14963             status = el_search_message(str, fh, FALSE);
14964 
14965          } while (status != EL_SUCCESS && (INT) ltime - (INT) lt < 3600 * 24 * 365);
14966 
14967          if (status != EL_SUCCESS)
14968             return EL_FIRST_MSG;
14969 
14970          /* adjust tag */
14971          strcpy(tag, str);
14972 
14973          /* go to end of current file */
14974          lseek(*fh, 0, SEEK_END);
14975       }
14976 
14977       /* read previous message size */
14978       lseek(*fh, -17, SEEK_CUR);
14979       i = read(*fh, str, 17);
14980       if (i <= 0) {
14981          close(*fh);
14982          return EL_FILE_ERROR;
14983       }
14984 
14985       if (strncmp(str, "$End$: ", 7) != 0) {
14986          close(*fh);
14987          return EL_FILE_ERROR;
14988       }
14989 
14990       /* make sure the input string to atoi() is zero-terminated:
14991        * $End$:      355garbage
14992        * 01234567890123456789 */
14993       str[15] = 0;
14994 
14995       size = atoi(str + 7);
14996       assert(size > 15);
14997 
14998       lseek(*fh, -size, SEEK_CUR);
14999 
15000       /* adjust tag */
15001       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15002    }
15003 
15004    if (direction == 1) {
15005       /* seek next message */
15006 
15007       /* read current message size */
15008       last = TELL(*fh);
15009 
15010       i = read(*fh, str, 15);
15011       if (i <= 0) {
15012          close(*fh);
15013          return EL_FILE_ERROR;
15014       }
15015       lseek(*fh, -15, SEEK_CUR);
15016 
15017       if (strncmp(str, "$Start$: ", 9) != 0) {
15018          close(*fh);
15019          return EL_FILE_ERROR;
15020       }
15021 
15022       /* make sure the input string to atoi() is zero-terminated
15023        * $Start$:    606garbage
15024        * 01234567890123456789 */
15025       str[15] = 0;
15026 
15027       size = atoi(str + 9);
15028       assert(size > 15);
15029 
15030       lseek(*fh, size, SEEK_CUR);
15031 
15032       /* if EOF, goto next day */
15033       i = read(*fh, str, 15);
15034       if (i < 15) {
15035          close(*fh);
15036          time((long *) &lact);
15037 
15038          lt = ltime;
15039          do {
15040             lt += 3600 * 24;
15041             tms = localtime((const time_t *) &lt);
15042             sprintf(str, "%02d%02d%02d.0",
15043                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15044 
15045             status = el_search_message(str, fh, FALSE);
15046 
15047          } while (status != EL_SUCCESS && (INT) lt - (INT) lact < 3600 * 24);
15048 
15049          if (status != EL_SUCCESS)
15050             return EL_LAST_MSG;
15051 
15052          /* adjust tag */
15053          strcpy(tag, str);
15054 
15055          /* go to beginning of current file */
15056          lseek(*fh, 0, SEEK_SET);
15057       } else
15058          lseek(*fh, -15, SEEK_CUR);
15059 
15060       /* adjust tag */
15061       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15062    }
15063 
15064    return EL_SUCCESS;
15065 }
15066 
15067 
15068 /********************************************************************/
15069 INT el_retrieve(char *tag, char *date, int *run, char *author, char *type,
15070                 char *system, char *subject, char *text, int *textsize,
15071                 char *orig_tag, char *reply_tag,
15072                 char *attachment1, char *attachment2, char *attachment3, char *encoding)
15073 /********************************************************************\
15074 
15075   Routine: el_retrieve
15076 
15077   Purpose: Retrieve an ELog entry by its message tab
15078 
15079   Input:
15080     char   *tag             tag in the form YYMMDD.offset
15081     int    *size            Size of text buffer
15082 
15083   Output:
15084     char   *tag             tag of retrieved message
15085     char   *date            Date/time of message recording
15086     int    *run             Run number
15087     char   *author          Message author
15088     char   *type            Message type
15089     char   *system          Message system
15090     char   *subject         Subject
15091     char   *text            Message text
15092     char   *orig_tag        Original message if this one is a reply
15093     char   *reply_tag       Reply for current message
15094     char   *attachment1/2/3 File attachment
15095     char   *encoding        Encoding of message
15096     int    *size            Actual message text size
15097 
15098   Function value:
15099     EL_SUCCESS              Successful completion
15100     EL_LAST_MSG             Last message in log
15101 
15102 \********************************************************************/
15103 {
15104    int size, fh = 0, offset, search_status, rd;
15105    char str[256], *p;
15106    char message[10000], thread[256], attachment_all[256];
15107 
15108    if (tag[0]) {
15109       search_status = el_search_message(tag, &fh, TRUE);
15110       if (search_status != EL_SUCCESS)
15111          return search_status;
15112    } else {
15113       /* open most recent message */
15114       strcpy(tag, "-1");
15115       search_status = el_search_message(tag, &fh, TRUE);
15116       if (search_status != EL_SUCCESS)
15117          return search_status;
15118    }
15119 
15120    /* extract message size */
15121    offset = TELL(fh);
15122    rd = read(fh, str, 15);
15123    assert(rd == 15);
15124 
15125    /* make sure the input string is zero-terminated before we call atoi() */
15126    str[15] = 0;
15127 
15128    /* get size */
15129    size = atoi(str + 9);
15130 
15131    assert(strncmp(str, "$Start$:", 8) == 0);
15132    assert(size > 15);
15133    assert(size < sizeof(message));
15134 
15135    memset(message, 0, sizeof(message));
15136 
15137    rd = read(fh, message, size);
15138    assert(rd > 0);
15139    assert((rd + 15 == size) || (rd == size));
15140 
15141    close(fh);
15142 
15143    /* decode message */
15144    if (strstr(message, "Run: ") && run)
15145       *run = atoi(strstr(message, "Run: ") + 5);
15146 
15147    el_decode(message, "Date: ", date, 80);      /* size from show_elog_submit_query() */
15148    el_decode(message, "Thread: ", thread, sizeof(thread));
15149    el_decode(message, "Author: ", author, 80);  /* size from show_elog_submit_query() */
15150    el_decode(message, "Type: ", type, 80);      /* size from show_elog_submit_query() */
15151    el_decode(message, "System: ", system, 80);  /* size from show_elog_submit_query() */
15152    el_decode(message, "Subject: ", subject, 256);       /* size from show_elog_submit_query() */
15153    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
15154    el_decode(message, "Encoding: ", encoding, 80);      /* size from show_elog_submit_query() */
15155 
15156    /* break apart attachements */
15157    if (attachment1 && attachment2 && attachment3) {
15158       attachment1[0] = attachment2[0] = attachment3[0] = 0;
15159       p = strtok(attachment_all, ",");
15160       if (p != NULL) {
15161          strcpy(attachment1, p);
15162          p = strtok(NULL, ",");
15163          if (p != NULL) {
15164             strcpy(attachment2, p);
15165             p = strtok(NULL, ",");
15166             if (p != NULL)
15167                strcpy(attachment3, p);
15168          }
15169       }
15170 
15171       assert(strlen(attachment1) < 256);        /* size from show_elog_submit_query() */
15172       assert(strlen(attachment2) < 256);        /* size from show_elog_submit_query() */
15173       assert(strlen(attachment3) < 256);        /* size from show_elog_submit_query() */
15174    }
15175 
15176    /* conver thread in reply-to and reply-from */
15177    if (orig_tag != NULL && reply_tag != NULL) {
15178       p = strtok(thread, " \r");
15179       if (p != NULL)
15180          strcpy(orig_tag, p);
15181       else
15182          strcpy(orig_tag, "");
15183       p = strtok(NULL, " \r");
15184       if (p != NULL)
15185          strcpy(reply_tag, p);
15186       else
15187          strcpy(reply_tag, "");
15188       if (atoi(orig_tag) == 0)
15189          orig_tag[0] = 0;
15190       if (atoi(reply_tag) == 0)
15191          reply_tag[0] = 0;
15192    }
15193 
15194    p = strstr(message, "========================================\n");
15195 
15196    if (text != NULL) {
15197       if (p != NULL) {
15198          p += 41;
15199          if ((int) strlen(p) >= *textsize) {
15200             strncpy(text, p, *textsize - 1);
15201             text[*textsize - 1] = 0;
15202             return EL_TRUNCATED;
15203          } else {
15204             strcpy(text, p);
15205 
15206             /* strip end tag */
15207             if (strstr(text, "$End$"))
15208                *strstr(text, "$End$") = 0;
15209 
15210             *textsize = strlen(text);
15211          }
15212       } else {
15213          text[0] = 0;
15214          *textsize = 0;
15215       }
15216    }
15217 
15218    if (search_status == EL_LAST_MSG)
15219       return EL_LAST_MSG;
15220 
15221    return EL_SUCCESS;
15222 }
15223 
15224 
15225 /********************************************************************/
15226 INT el_search_run(int run, char *return_tag)
15227 /********************************************************************\
15228 
15229   Routine: el_search_run
15230 
15231   Purpose: Find first message belonging to a specific run
15232 
15233   Input:
15234     int    run              Run number
15235 
15236   Output:
15237     char   *tag             tag of retrieved message
15238 
15239   Function value:
15240     EL_SUCCESS              Successful completion
15241     EL_LAST_MSG             Last message in log
15242 
15243 \********************************************************************/
15244 {
15245    int actual_run, fh, status;
15246    char tag[256];
15247 
15248    tag[0] = return_tag[0] = 0;
15249 
15250    do {
15251       /* open first message in file */
15252       strcat(tag, "-1");
15253       status = el_search_message(tag, &fh, TRUE);
15254       if (status == EL_FIRST_MSG)
15255          break;
15256       if (status != EL_SUCCESS)
15257          return status;
15258       close(fh);
15259 
15260       if (strchr(tag, '.') != NULL)
15261          strcpy(strchr(tag, '.'), ".0");
15262 
15263       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15264                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15265    } while (actual_run >= run);
15266 
15267    while (actual_run < run) {
15268       strcat(tag, "+1");
15269       status = el_search_message(tag, &fh, TRUE);
15270       if (status == EL_LAST_MSG)
15271          break;
15272       if (status != EL_SUCCESS)
15273          return status;
15274       close(fh);
15275 
15276       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15277                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15278    }
15279 
15280    strcpy(return_tag, tag);
15281 
15282    if (status == EL_LAST_MSG || status == EL_FIRST_MSG)
15283       return status;
15284 
15285    return EL_SUCCESS;
15286 }
15287 
15288 
15289 /********************************************************************/
15290 INT el_delete_message(char *tag)
15291 /********************************************************************\
15292 
15293   Routine: el_submit
15294 
15295   Purpose: Submit an ELog entry
15296 
15297   Input:
15298     char   *tag             Message tage
15299 
15300   Output:
15301     <none>
15302 
15303   Function value:
15304     EL_SUCCESS              Successful completion
15305 
15306 \********************************************************************/
15307 {
15308 #ifdef LOCAL_ROUTINES
15309    INT n, size, fh, mutex, offset = 0, tail_size, status;
15310    char dir[256], str[256], file_name[256];
15311    HNDLE hDB;
15312    char *buffer = NULL;
15313 
15314    cm_get_experiment_database(&hDB, NULL);
15315 
15316    /* request semaphore */
15317    cm_get_experiment_mutex(NULL, &mutex);
15318    status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
15319    if (status != SS_SUCCESS) {
15320       cm_msg(MERROR, "el_delete_message",
15321              "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
15322       abort();
15323    }
15324 
15325 
15326    /* generate file name YYMMDD.log in data directory */
15327    cm_get_experiment_database(&hDB, NULL);
15328 
15329    size = sizeof(dir);
15330    memset(dir, 0, size);
15331    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15332    if (status != DB_SUCCESS)
15333       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15334 
15335    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15336       strcat(dir, DIR_SEPARATOR_STR);
15337 
15338    strcpy(str, tag);
15339    if (strchr(str, '.')) {
15340       offset = atoi(strchr(str, '.') + 1);
15341       *strchr(str, '.') = 0;
15342    }
15343    sprintf(file_name, "%s%s.log", dir, str);
15344    fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15345    if (fh < 0) {
15346       ss_mutex_release(mutex);
15347       return EL_FILE_ERROR;
15348    }
15349    lseek(fh, offset, SEEK_SET);
15350    read(fh, str, 16);
15351    size = atoi(str + 9);
15352 
15353    /* buffer tail of logfile */
15354    lseek(fh, 0, SEEK_END);
15355    tail_size = TELL(fh) - (offset + size);
15356 
15357    if (tail_size > 0) {
15358       buffer = (char *) M_MALLOC(tail_size);
15359       if (buffer == NULL) {
15360          close(fh);
15361          ss_mutex_release(mutex);
15362          return EL_FILE_ERROR;
15363       }
15364 
15365       lseek(fh, offset + size, SEEK_SET);
15366       n = read(fh, buffer, tail_size);
15367    }
15368    lseek(fh, offset, SEEK_SET);
15369 
15370    if (tail_size > 0) {
15371       n = write(fh, buffer, tail_size);
15372       M_FREE(buffer);
15373    }
15374 
15375    /* truncate file here */
15376 #ifdef OS_WINNT
15377    chsize(fh, TELL(fh));
15378 #else
15379    ftruncate(fh, TELL(fh));
15380 #endif
15381 
15382    /* if file length gets zero, delete file */
15383    tail_size = lseek(fh, 0, SEEK_END);
15384    close(fh);
15385 
15386    if (tail_size == 0)
15387       remove(file_name);
15388 
15389    /* release elog mutex */
15390    ss_mutex_release(mutex);
15391 #endif                          /* LOCAL_ROUTINES */
15392 
15393    return EL_SUCCESS;
15394 }
15395 
15396 /**dox***************************************************************/
15397 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15398 
15399 /**dox***************************************************************/
15400                             /** @} *//* end of elfunctionc */
15401 
15402 /**dox***************************************************************/
15403 /** @addtogroup alfunctionc
15404  *  
15405  *  @{  */
15406 
15407 /**dox***************************************************************/
15408 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15409 
15410 /********************************************************************\
15411 *                                                                    *
15412 *                     Alarm functions                                *
15413 *                                                                    *
15414 \********************************************************************/
15415 
15416 /********************************************************************/
15417 BOOL al_evaluate_condition(char *condition, char *value)
15418 {
15419    HNDLE hDB, hkey;
15420    int i, j, index, size;
15421    KEY key;
15422    double value1, value2;
15423    char value1_str[256], value2_str[256], str[256], op[3], function[80];
15424    char data[10000];
15425    DWORD dtime;
15426 
15427    strcpy(str, condition);
15428    op[1] = op[2] = 0;
15429    value1 = value2 = 0;
15430    index = 0;
15431 
15432    /* find value and operator */
15433    for (i = strlen(str) - 1; i > 0; i--)
15434       if (strchr("<>=!", str[i]) != NULL)
15435          break;
15436    op[0] = str[i];
15437    for (j = 1; str[i + j] == ' '; j++);
15438    strlcpy(value2_str, str + i + j, sizeof(value2_str));
15439    str[i] = 0;
15440 
15441    if (i > 0 && strchr("<>=!", str[i - 1])) {
15442       op[1] = op[0];
15443       op[0] = str[--i];
15444       str[i] = 0;
15445    }
15446 
15447    i--;
15448    while (i > 0 && str[i] == ' ')
15449       i--;
15450    str[i + 1] = 0;
15451 
15452    /* check if function */
15453    function[0] = 0;
15454    if (str[i] == ')') {
15455       str[i--] = 0;
15456       if (strchr(str, '(')) {
15457          *strchr(str, '(') = 0;
15458          strcpy(function, str);
15459          for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
15460             str[j] = str[i];
15461          str[j] = 0;
15462          i = j - 1;
15463       }
15464    }
15465 
15466    /* find key */
15467    if (str[i] == ']') {
15468       str[i--] = 0;
15469       while (i > 0 && isdigit(str[i]))
15470          i--;
15471       index = atoi(str + i + 1);
15472       str[i] = 0;
15473    }
15474 
15475    cm_get_experiment_database(&hDB, NULL);
15476    db_find_key(hDB, 0, str, &hkey);
15477    if (!hkey) {
15478       cm_msg(MERROR, "al_evaluate_condition",
15479              "Cannot find key %s to evaluate alarm condition", str);
15480       if (value)
15481          strcpy(value, "unknown");
15482       return FALSE;
15483    }
15484 
15485    if (equal_ustring(function, "access")) {
15486       /* check key access time */
15487       db_get_key_time(hDB, hkey, &dtime);
15488       sprintf(value1_str, "%d", dtime);
15489       value1 = atof(value1_str);
15490    } else {
15491       /* get key data and convert to double */
15492       db_get_key(hDB, hkey, &key);
15493       size = sizeof(data);
15494       db_get_data(hDB, hkey, data, &size, key.type);
15495       db_sprintf(value1_str, data, size, index, key.type);
15496       value1 = atof(value1_str);
15497    }
15498 
15499    /* convert boolean values to integers */
15500    if (key.type == TID_BOOL) {
15501       value1 = (value1_str[0] == 'Y' || value1_str[0] == 'y' || value1_str[0] == '1');
15502       value2 = (value2_str[0] == 'Y' || value2_str[0] == 'y' || value2_str[0] == '1');
15503    }
15504 
15505    /* return value */
15506    if (value)
15507       strcpy(value, value1_str);
15508 
15509    /* now do logical operation */
15510    if (strcmp(op, "=") == 0)
15511       return value1 == value2;
15512    if (strcmp(op, "==") == 0)
15513       return value1 == value2;
15514    if (strcmp(op, "!=") == 0)
15515       return value1 != value2;
15516    if (strcmp(op, "<") == 0)
15517       return value1 < value2;
15518    if (strcmp(op, ">") == 0)
15519       return value1 > value2;
15520    if (strcmp(op, "<=") == 0)
15521       return value1 <= value2;
15522    if (strcmp(op, ">=") == 0)
15523       return value1 >= value2;
15524 
15525    return FALSE;
15526 }
15527 
15528 /**dox***************************************************************/
15529 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15530 
15531 /********************************************************************/
15532 /**
15533 Trigger a certain alarm.
15534 \code  ...
15535   lazy.alarm[0] = 0;
15536   size = sizeof(lazy.alarm);
15537   db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
15538 
15539   // trigger alarm if defined
15540   if (lazy.alarm[0])
15541     al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
15542   ...
15543 \endcode
15544 @param alarm_name Alarm name, defined in /alarms/alarms
15545 @param alarm_message Optional message which goes with alarm
15546 @param default_class If alarm is not yet defined under
15547                     /alarms/alarms/<alarm_name>, a new one
15548                     is created and this default class is used.
15549 @param cond_str String displayed in alarm condition
15550 @param type Alarm type, one of AT_xxx
15551 @return AL_SUCCESS, AL_INVALID_NAME
15552 */
15553 INT al_trigger_alarm(char *alarm_name, char *alarm_message, char *default_class,
15554                      char *cond_str, INT type)
15555 {
15556    if (rpc_is_remote())
15557       return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name, alarm_message,
15558                       default_class, cond_str, type);
15559 
15560 #ifdef LOCAL_ROUTINES
15561    {
15562       int status, size;
15563       HNDLE hDB, hkeyalarm;
15564       char str[256];
15565       ALARM alarm;
15566       BOOL flag;
15567       ALARM_ODB_STR(alarm_odb_str);
15568 
15569       cm_get_experiment_database(&hDB, NULL);
15570 
15571       /* check online mode */
15572       flag = TRUE;
15573       size = sizeof(flag);
15574       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
15575       if (!flag)
15576          return AL_SUCCESS;
15577 
15578       /* find alarm */
15579       sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15580       db_find_key(hDB, 0, str, &hkeyalarm);
15581       if (!hkeyalarm) {
15582          /* alarm must be an internal analyzer alarm, so create a default alarm */
15583          status = db_create_record(hDB, 0, str, strcomb(alarm_odb_str));
15584          db_find_key(hDB, 0, str, &hkeyalarm);
15585          if (!hkeyalarm) {
15586             cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record");
15587             return AL_ERROR_ODB;
15588          }
15589 
15590          if (default_class && default_class[0])
15591             db_set_value(hDB, hkeyalarm, "Alarm Class", default_class, 32, 1, TID_STRING);
15592          status = TRUE;
15593          db_set_value(hDB, hkeyalarm, "Active", &status, sizeof(status), 1, TID_BOOL);
15594       }
15595 
15596       /* set parameters for internal alarms */
15597       if (type != AT_EVALUATED && type != AT_PERIODIC) {
15598          db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
15599          strcpy(str, cond_str);
15600          db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
15601       }
15602 
15603       size = sizeof(alarm);
15604       status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15605       if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15606          /* make sure alarm record has right structure */
15607          db_check_record(hDB, hkeyalarm, "", strcomb(alarm_odb_str), TRUE);
15608 
15609          size = sizeof(alarm);
15610          status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15611          if (status != DB_SUCCESS) {
15612             cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record");
15613             return AL_ERROR_ODB;
15614          }
15615       }
15616 
15617       /* if internal alarm, check if active and check interval */
15618       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15619          /* check global alarm flag */
15620          flag = TRUE;
15621          size = sizeof(flag);
15622          db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL,
15623                       TRUE);
15624          if (!flag)
15625             return AL_SUCCESS;
15626 
15627          if (!alarm.active)
15628             return AL_SUCCESS;
15629 
15630          if ((INT) ss_time() - (INT) alarm.checked_last < alarm.check_interval)
15631             return AL_SUCCESS;
15632 
15633          /* now the alarm will be triggered, so save time */
15634          alarm.checked_last = ss_time();
15635       }
15636 
15637       /* write back alarm message for internal alarms */
15638       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15639          strncpy(alarm.alarm_message, alarm_message, 79);
15640          alarm.alarm_message[79] = 0;
15641       }
15642 
15643       /* now trigger alarm class defined in this alarm */
15644       if (alarm.alarm_class[0])
15645          al_trigger_class(alarm.alarm_class, alarm_message, alarm.triggered > 0);
15646 
15647       /* signal alarm being triggered */
15648       cm_asctime(str, sizeof(str));
15649 
15650       if (!alarm.triggered)
15651          strcpy(alarm.time_triggered_first, str);
15652 
15653       alarm.triggered++;
15654       strcpy(alarm.time_triggered_last, str);
15655 
15656       alarm.checked_last = ss_time();
15657 
15658       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
15659       if (status != DB_SUCCESS) {
15660          cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record");
15661          return AL_ERROR_ODB;
15662       }
15663 
15664    }
15665 #endif                          /* LOCAL_ROUTINES */
15666 
15667    return AL_SUCCESS;
15668 }
15669 
15670 /**dox***************************************************************/
15671 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15672 
15673 /********************************************************************/
15674 INT al_trigger_class(char *alarm_class, char *alarm_message, BOOL first)
15675 /********************************************************************\
15676 
15677   Routine: al_trigger_class
15678 
15679   Purpose: Trigger a certain alarm class
15680 
15681   Input:
15682     char   *alarm_class     Alarm class, must be defined in
15683                             /alarms/classes
15684     char   *alarm_message   Optional message which goes with alarm
15685     BOOL   first            TRUE if alarm is triggered first time
15686                             (used for elog)
15687 
15688   Output:
15689 
15690   Function value:
15691     AL_INVALID_NAME         Alarm class not defined
15692     AL_SUCCESS              Successful completion
15693 
15694 \********************************************************************/
15695 {
15696    int status, size, state;
15697    HNDLE hDB, hkeyclass;
15698    char str[256], command[256], tag[32];
15699    ALARM_CLASS ac;
15700 
15701    cm_get_experiment_database(&hDB, NULL);
15702 
15703    /* get alarm class */
15704    sprintf(str, "/Alarms/Classes/%s", alarm_class);
15705    db_find_key(hDB, 0, str, &hkeyclass);
15706    if (!hkeyclass) {
15707       cm_msg(MERROR, "al_trigger_class", "Alarm class %s not found in ODB", alarm_class);
15708       return AL_INVALID_NAME;
15709    }
15710 
15711    size = sizeof(ac);
15712    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
15713    if (status != DB_SUCCESS) {
15714       cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record");
15715       return AL_ERROR_ODB;
15716    }
15717 
15718    /* write system message */
15719    if (ac.write_system_message &&
15720        (INT) ss_time() - (INT) ac.system_message_last > ac.system_message_interval) {
15721       sprintf(str, "%s: %s", alarm_class, alarm_message);
15722       cm_msg(MTALK, "al_trigger_class", str);
15723       ac.system_message_last = ss_time();
15724    }
15725 
15726    /* write elog message on first trigger */
15727    if (ac.write_elog_message && first)
15728       el_submit(0, "Alarm system", "Alarm", "General", alarm_class, str,
15729                 "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, 32);
15730 
15731    /* execute command */
15732    if (ac.execute_command[0] &&
15733        ac.execute_interval > 0 &&
15734        (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
15735       sprintf(str, "%s: %s", alarm_class, alarm_message);
15736       sprintf(command, ac.execute_command, str);
15737       cm_msg(MINFO, "al_trigger_class", "Execute: %s", command);
15738       ss_system(command);
15739       ac.execute_last = ss_time();
15740    }
15741 
15742    /* stop run */
15743    if (ac.stop_run) {
15744       state = STATE_STOPPED;
15745       size = sizeof(state);
15746       db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
15747       if (state != STATE_STOPPED)
15748          cm_transition(TR_STOP, 0, NULL, 0, ASYNC, FALSE);
15749    }
15750 
15751    status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
15752    if (status != DB_SUCCESS) {
15753       cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
15754       return AL_ERROR_ODB;
15755    }
15756 
15757    return AL_SUCCESS;
15758 }
15759 
15760 
15761 /********************************************************************/
15762 INT al_reset_alarm(char *alarm_name)
15763 /********************************************************************\
15764 
15765   Routine: al_reset_alarm
15766 
15767   Purpose: Reset (acknowledge) alarm
15768 
15769   Input:
15770     char   *alarm_name      Alarm name, must be defined in /Alarms/Alarms
15771                             If NULL reset all alarms
15772 
15773   Output:
15774     <none>
15775 
15776   Function value:
15777     AL_INVALID_NAME         Alarm name not defined
15778     AL_RESET                Alarm was triggered and reset
15779     AL_SUCCESS              Successful completion
15780 
15781 \********************************************************************/
15782 {
15783    int status, size, i;
15784    HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
15785    KEY key;
15786    char str[256];
15787    ALARM alarm;
15788    ALARM_CLASS ac;
15789 
15790    cm_get_experiment_database(&hDB, NULL);
15791 
15792    if (alarm_name == NULL) {
15793       /* reset all alarms */
15794       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
15795       if (hkeyalarm) {
15796          for (i = 0;; i++) {
15797             db_enum_link(hDB, hkeyalarm, i, &hsubkey);
15798 
15799             if (!hsubkey)
15800                break;
15801 
15802             db_get_key(hDB, hsubkey, &key);
15803             al_reset_alarm(key.name);
15804          }
15805       }
15806       return AL_SUCCESS;
15807    }
15808 
15809    /* find alarm and alarm class */
15810    sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15811    db_find_key(hDB, 0, str, &hkeyalarm);
15812    if (!hkeyalarm) {
15813       cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB", alarm_name);
15814       return AL_INVALID_NAME;
15815    }
15816 
15817    size = sizeof(alarm);
15818    status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15819    if (status != DB_SUCCESS) {
15820       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
15821       return AL_ERROR_ODB;
15822    }
15823 
15824    sprintf(str, "/Alarms/Classes/%s", alarm.alarm_class);
15825    db_find_key(hDB, 0, str, &hkeyclass);
15826    if (!hkeyclass) {
15827       cm_msg(MERROR, "al_reset_alarm", "Alarm class %s not found in ODB",
15828              alarm.alarm_class);
15829       return AL_INVALID_NAME;
15830    }
15831 
15832    size = sizeof(ac);
15833    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
15834    if (status != DB_SUCCESS) {
15835       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
15836       return AL_ERROR_ODB;
15837    }
15838 
15839    if (alarm.triggered) {
15840       alarm.triggered = 0;
15841       alarm.time_triggered_first[0] = 0;
15842       alarm.time_triggered_last[0] = 0;
15843       alarm.checked_last = 0;
15844 
15845       ac.system_message_last = 0;
15846       ac.execute_last = 0;
15847 
15848       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
15849       if (status != DB_SUCCESS) {
15850          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
15851          return AL_ERROR_ODB;
15852       }
15853       status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
15854       if (status != DB_SUCCESS) {
15855          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
15856          return AL_ERROR_ODB;
15857       }
15858       return AL_RESET;
15859    }
15860 
15861    return AL_SUCCESS;
15862 }
15863 
15864 
15865 /********************************************************************/
15866 INT al_check()
15867 /********************************************************************\
15868 
15869   Routine: al_scan
15870 
15871   Purpose: Scan ODB alarams and programs
15872 
15873   Input:
15874 
15875   Output:
15876 
15877   Function value:
15878     AL_SUCCESS              Successful completion
15879 
15880 \********************************************************************/
15881 {
15882    if (rpc_is_remote())
15883       return rpc_call(RPC_AL_CHECK);
15884 
15885 #ifdef LOCAL_ROUTINES
15886    {
15887       INT i, status, size, mutex;
15888       HNDLE hDB, hkeyroot, hkey;
15889       KEY key;
15890       ALARM alarm;
15891       char str[256], value[256];
15892       time_t now;
15893       PROGRAM_INFO program_info;
15894       BOOL flag;
15895 
15896       ALARM_CLASS_STR(alarm_class_str);
15897       ALARM_ODB_STR(alarm_odb_str);
15898       ALARM_PERIODIC_STR(alarm_periodic_str);
15899 
15900       cm_get_experiment_database(&hDB, NULL);
15901 
15902       if (hDB == 0)
15903          return AL_SUCCESS;     /* called from server not yet connected */
15904 
15905       /* check online mode */
15906       flag = TRUE;
15907       size = sizeof(flag);
15908       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
15909       if (!flag)
15910          return AL_SUCCESS;
15911 
15912       /* check global alarm flag */
15913       flag = TRUE;
15914       size = sizeof(flag);
15915       db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
15916       if (!flag)
15917          return AL_SUCCESS;
15918 
15919       /* request semaphore */
15920       cm_get_experiment_mutex(&mutex, NULL);
15921       status = ss_mutex_wait_for(mutex, 100);
15922       if (status != SS_SUCCESS)
15923          return SUCCESS;        /* someone else is doing alarm business */
15924 
15925       /* check ODB alarms */
15926       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15927       if (!hkeyroot) {
15928          /* create default ODB alarm */
15929          status =
15930              db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB", strcomb(alarm_odb_str));
15931          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15932          if (!hkeyroot) {
15933             ss_mutex_release(mutex);
15934             return SUCCESS;
15935          }
15936 
15937          status =
15938              db_create_record(hDB, 0, "/Alarms/Alarms/Demo periodic",
15939                               strcomb(alarm_periodic_str));
15940          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15941          if (!hkeyroot) {
15942             ss_mutex_release(mutex);
15943             return SUCCESS;
15944          }
15945 
15946          /* create default alarm classes */
15947          status =
15948              db_create_record(hDB, 0, "/Alarms/Classes/Alarm", strcomb(alarm_class_str));
15949          status =
15950              db_create_record(hDB, 0, "/Alarms/Classes/Warning",
15951                               strcomb(alarm_class_str));
15952          if (status != DB_SUCCESS) {
15953             ss_mutex_release(mutex);
15954             return SUCCESS;
15955          }
15956       }
15957 
15958       for (i = 0;; i++) {
15959          status = db_enum_key(hDB, hkeyroot, i, &hkey);
15960          if (status == DB_NO_MORE_SUBKEYS)
15961             break;
15962 
15963          db_get_key(hDB, hkey, &key);
15964 
15965          size = sizeof(alarm);
15966          status = db_get_record(hDB, hkey, &alarm, &size, 0);
15967          if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15968             /* make sure alarm record has right structure */
15969             db_check_record(hDB, hkey, "", strcomb(alarm_odb_str), TRUE);
15970             size = sizeof(alarm);
15971             status = db_get_record(hDB, hkey, &alarm, &size, 0);
15972             if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15973                cm_msg(MERROR, "al_check", "Cannot get alarm record");
15974                continue;
15975             }
15976          }
15977 
15978          /* check periodic alarm only when active */
15979          if (alarm.active &&
15980              alarm.type == AT_PERIODIC &&
15981              alarm.check_interval > 0 &&
15982              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
15983             /* if checked_last has not been set, set it to current time */
15984             if (alarm.checked_last == 0) {
15985                alarm.checked_last = ss_time();
15986                db_set_record(hDB, hkey, &alarm, size, 0);
15987             } else
15988                al_trigger_alarm(key.name, alarm.alarm_message, alarm.alarm_class, "",
15989                                 AT_PERIODIC);
15990          }
15991 
15992          /* check alarm only when active and not internal */
15993          if (alarm.active &&
15994              alarm.type == AT_EVALUATED &&
15995              alarm.check_interval > 0 &&
15996              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
15997             /* if condition is true, trigger alarm */
15998             if (al_evaluate_condition(alarm.condition, value)) {
15999                sprintf(str, alarm.alarm_message, value);
16000                al_trigger_alarm(key.name, str, alarm.alarm_class, "", AT_EVALUATED);
16001             } else {
16002                alarm.checked_last = ss_time();
16003                status = db_set_record(hDB, hkey, &alarm, sizeof(alarm), 0);
16004                if (status != DB_SUCCESS) {
16005                   cm_msg(MERROR, "al_check", "Cannot write back alarm record");
16006                   continue;
16007                }
16008             }
16009          }
16010       }
16011 
16012       /* check /programs alarms */
16013       db_find_key(hDB, 0, "/Programs", &hkeyroot);
16014       if (hkeyroot) {
16015          for (i = 0;; i++) {
16016             status = db_enum_key(hDB, hkeyroot, i, &hkey);
16017             if (status == DB_NO_MORE_SUBKEYS)
16018                break;
16019 
16020             db_get_key(hDB, hkey, &key);
16021 
16022             /* don't check "execute on xxx" */
16023             if (key.type != TID_KEY)
16024                continue;
16025 
16026             size = sizeof(program_info);
16027             status = db_get_record(hDB, hkey, &program_info, &size, 0);
16028             if (status != DB_SUCCESS) {
16029                cm_msg(MERROR, "al_check", "Cannot get program info record");
16030                continue;
16031             }
16032 
16033             now = ss_time();
16034 
16035             rpc_get_name(str);
16036             str[strlen(key.name)] = 0;
16037             if (!equal_ustring(str, key.name) &&
16038                 cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
16039                if (program_info.first_failed == 0)
16040                   program_info.first_failed = now;
16041 
16042                /* fire alarm when not running for more than what specified in check interval */
16043                if (now - program_info.first_failed >= program_info.check_interval / 1000) {
16044                   /* if not running and alarm calss defined, trigger alarm */
16045                   if (program_info.alarm_class[0]) {
16046                      sprintf(str, "Program %s is not running", key.name);
16047                      al_trigger_alarm(key.name, str, program_info.alarm_class,
16048                                       "Program not running", AT_PROGRAM);
16049                   }
16050 
16051                   /* auto restart program */
16052                   if (program_info.auto_restart && program_info.start_command[0]) {
16053                      ss_system(program_info.start_command);
16054                      program_info.first_failed = 0;
16055                      cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
16056                   }
16057                }
16058             } else
16059                program_info.first_failed = 0;
16060 
16061             db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
16062          }
16063       }
16064 
16065       ss_mutex_release(mutex);
16066    }
16067 #endif                          /* LOCAL_COUTINES */
16068 
16069    return SUCCESS;
16070 }
16071 
16072 /**dox***************************************************************/
16073 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16074 
16075                             /** @} *//* end of alfunctionc */
16076 
16077 /***** sKIP eb_xxx **************************************************/
16078 /**dox***************************************************************/
16079 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16080 /***** sKIP eb_xxx **************************************************/
16081 
16082 #if !defined(OS_VXWORKS)
16083 /********************************************************************\
16084 *                                                                    *
16085 *                 Event buffer functions                             *
16086 *                                                                    *
16087 \********************************************************************/
16088 
16089 /* PAA several modification in the eb_xxx()
16090    also new function eb_buffer_full()
16091 */
16092 static char *_event_ring_buffer = NULL;
16093 static INT _eb_size;
16094 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
16095 
16096 /********************************************************************/
16097 INT eb_create_buffer(INT size)
16098 /********************************************************************\
16099 
16100   Routine: eb_create_buffer
16101 
16102   Purpose: Create an event buffer. Has to be called initially before
16103            any other eb_xxx function
16104 
16105   Input:
16106     INT    size             Size in bytes
16107 
16108   Output:
16109     none
16110 
16111   Function value:
16112     CM_SUCCESS              Successful completion
16113     BM_NO_MEMEORY           Out of memory
16114 
16115 \********************************************************************/
16116 {
16117    _event_ring_buffer = (char *) M_MALLOC(size);
16118    if (_event_ring_buffer == NULL)
16119       return BM_NO_MEMORY;
16120 
16121    memset(_event_ring_buffer, 0, size);
16122    _eb_size = size;
16123 
16124    _eb_write_pointer = _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16125 
16126    _send_sock = rpc_get_event_sock();
16127 
16128    return CM_SUCCESS;
16129 }
16130 
16131 /********************************************************************/
16132 INT eb_free_buffer()
16133 /********************************************************************\
16134 
16135   Routine: eb_free_buffer
16136 
16137   Purpose: Free memory allocated voa eb_create_buffer
16138 
16139   Input:
16140     none
16141 
16142   Output:
16143     none
16144 
16145   Function value:
16146     CM_SUCCESS              Successful completion
16147 
16148 \********************************************************************/
16149 {
16150    if (_event_ring_buffer)
16151       M_FREE(_event_ring_buffer);
16152 
16153    _eb_size = 0;
16154    return CM_SUCCESS;
16155 }
16156 
16157 
16158 /********************************************************************/
16159 INT eb_free_space(void)
16160 /********************************************************************\
16161 
16162   Routine: eb_free_space
16163 
16164   Purpose: Compute and return usable free space in the event buffer
16165 
16166   Input:
16167     none
16168 
16169   Output:
16170     none
16171 
16172   Function value:
16173     INT    Number of usable free bytes in the event buffer
16174 
16175 \********************************************************************/
16176 {
16177    INT free;
16178 
16179    if (_event_ring_buffer == NULL) {
16180       cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
16181       return -1;
16182    }
16183 
16184    if (_eb_write_pointer >= _eb_read_pointer) {
16185       free = _eb_size - ((POINTER_T) _eb_write_pointer - (POINTER_T) _event_ring_buffer);
16186    } else if (_eb_write_pointer >= _event_ring_buffer) {
16187       free = (POINTER_T) _eb_read_pointer - (POINTER_T) _eb_write_pointer;
16188    } else if (_eb_end_pointer == _event_ring_buffer) {
16189       _eb_write_pointer = _event_ring_buffer;
16190       free = _eb_size;
16191    } else if (_eb_read_pointer == _event_ring_buffer) {
16192       free = 0;
16193    } else {
16194       _eb_write_pointer = _event_ring_buffer;
16195       free = (POINTER_T) _eb_read_pointer - (POINTER_T) _eb_write_pointer;
16196    }
16197 
16198    return free;
16199 }
16200 
16201 
16202 /********************************************************************/
16203 DWORD eb_get_level()
16204 /********************************************************************\
16205 
16206   Routine: eb_get_level
16207 
16208   Purpose: Return filling level of event buffer in percent
16209 
16210   Input:
16211     none
16212 
16213   Output:
16214     none
16215 
16216   Function value:
16217     DWORD level              0..99
16218 
16219 \********************************************************************/
16220 {
16221    INT size;
16222 
16223    size = _eb_size - eb_free_space();
16224 
16225    return (100 * size) / _eb_size;
16226 }
16227 
16228 
16229 /********************************************************************/
16230 BOOL eb_buffer_full(void)
16231 /********************************************************************\
16232 
16233   Routine: eb_buffer_full
16234 
16235   Purpose: Test if there is sufficient space in the event buffer
16236     for another event
16237 
16238   Input:
16239     none
16240 
16241   Output:
16242     none
16243 
16244   Function value:
16245     BOOL  Is there enough space for another event in the event buffer
16246 
16247 \********************************************************************/
16248 {
16249    INT free;
16250 
16251    free = eb_free_space();
16252 
16253    /* if max. event won't fit, return zero */
16254    return (free < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
16255 }
16256 
16257 
16258 /********************************************************************/
16259 EVENT_HEADER *eb_get_pointer()
16260 /********************************************************************\
16261 
16262   Routine: eb_get_pointer
16263 
16264   Purpose: Get pointer to next free location in event buffer
16265 
16266   Input:
16267     none
16268 
16269   Output:
16270     none
16271 
16272   Function value:
16273     EVENT_HEADER *            Pointer to free location
16274 
16275 \********************************************************************/
16276 {
16277    /* if max. event won't fit, return zero */
16278    if (eb_buffer_full()) {
16279 #ifdef OS_VXWORKS
16280       logMsg("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
16281              _eb_read_pointer - _event_ring_buffer,
16282              _eb_write_pointer - _event_ring_buffer,
16283              _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
16284 #endif
16285       return NULL;
16286    }
16287 
16288    /* leave space for buffer handle */
16289    return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
16290 }
16291 
16292 
16293 /********************************************************************/
16294 INT eb_increment_pointer(INT buffer_handle, INT event_size)
16295 /********************************************************************\
16296 
16297   Routine: eb_increment_pointer
16298 
16299   Purpose: Increment write pointer of event buffer after an event
16300            has been copied into the buffer (at an address previously
16301            obtained via eb_get_pointer)
16302 
16303   Input:
16304     INT buffer_handle         Buffer handle event should be sent to
16305     INT event_size            Event size in bytes including header
16306 
16307   Output:
16308     none
16309 
16310   Function value:
16311     CM_SUCCESS                Successful completion
16312 
16313 \********************************************************************/
16314 {
16315    INT aligned_event_size;
16316 
16317    /* if not connected remotely, use bm_send_event */
16318    if (_send_sock == 0)
16319       return bm_send_event(buffer_handle,
16320                            _eb_write_pointer + sizeof(INT), event_size, SYNC);
16321 
16322    aligned_event_size = ALIGN8(event_size);
16323 
16324    /* copy buffer handle */
16325    *((INT *) _eb_write_pointer) = buffer_handle;
16326    _eb_write_pointer += sizeof(INT) + aligned_event_size;
16327 
16328    if (_eb_write_pointer > _eb_end_pointer)
16329       _eb_end_pointer = _eb_write_pointer;
16330 
16331    if (_eb_write_pointer > _event_ring_buffer + _eb_size)
16332       cm_msg(MERROR, "eb_increment_pointer",
16333              "event size (%d) exeeds maximum event size (%d)", event_size,
16334              MAX_EVENT_SIZE);
16335 
16336    if (_eb_size - ((POINTER_T) _eb_write_pointer - (POINTER_T) _event_ring_buffer) <
16337        MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT)) {
16338       _eb_write_pointer = _event_ring_buffer;
16339 
16340       /* avoid rp==wp */
16341       if (_eb_read_pointer == _event_ring_buffer)
16342          _eb_write_pointer--;
16343    }
16344 
16345    return CM_SUCCESS;
16346 }
16347 
16348 
16349 /********************************************************************/
16350 INT eb_send_events(BOOL send_all)
16351 /********************************************************************\
16352 
16353   Routine: eb_send_events
16354 
16355   Purpose: Send events from the event buffer to the server
16356 
16357   Input:
16358     BOOL send_all             If FALSE, only send events if buffer
16359                               contains more than _opt_tcp_size bytes
16360 
16361   Output:
16362     none
16363 
16364   Function value:
16365     CM_SUCCESS                Successful completion
16366 
16367 \********************************************************************/
16368 {
16369    char *eb_wp, *eb_ep;
16370    INT size, i;
16371 
16372    /* write pointers are volatile, so make copy */
16373    eb_ep = _eb_end_pointer;
16374    eb_wp = _eb_write_pointer;
16375 
16376    if (eb_wp == _eb_read_pointer)
16377       return CM_SUCCESS;
16378    if (eb_wp > _eb_read_pointer) {
16379       size = (POINTER_T) eb_wp - (POINTER_T) _eb_read_pointer;
16380 
16381       /* don't send if less than optimal TCP buffer size available */
16382       if (size < (INT) _opt_tcp_size && !send_all)
16383          return CM_SUCCESS;
16384    } else {
16385       /* send last piece of event buffer */
16386       size = (POINTER_T) eb_ep - (POINTER_T) _eb_read_pointer;
16387    }
16388 
16389    while (size > _opt_tcp_size) {
16390       /* send buffer */
16391       i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
16392       if (i < 0) {
16393          printf("send_tcp() returned %d\n", i);
16394          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16395          return RPC_NET_ERROR;
16396       }
16397 
16398       _eb_read_pointer += _opt_tcp_size;
16399       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16400          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16401 
16402       size -= _opt_tcp_size;
16403    }
16404 
16405    if (send_all || eb_wp < _eb_read_pointer) {
16406       /* send buffer */
16407       i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
16408       if (i < 0) {
16409          printf("send_tcp() returned %d\n", i);
16410          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16411          return RPC_NET_ERROR;
16412       }
16413 
16414       _eb_read_pointer += size;
16415       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16416          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16417    }
16418 
16419    /* Check for case where eb_wp = eb_ring_buffer - 1 */
16420    if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
16421       return CM_SUCCESS;
16422    }
16423 
16424    if (eb_wp != _eb_read_pointer)
16425       return BM_MORE_EVENTS;
16426 
16427    return CM_SUCCESS;
16428 }
16429 
16430 #endif                          /* OS_VXWORKS  eb section */
16431 
16432 /**dox***************************************************************/
16433 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16434 
16435 /**dox***************************************************************/
16436 /** @addtogroup dmfunctionc
16437  *  
16438  *  @{  */
16439 
16440 /**dox***************************************************************/
16441 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16442 
16443 /********************************************************************\
16444 *                                                                    *
16445 *                 Dual memory buffer functions                       *
16446 *                                                                    *
16447 * Provide a dual memory buffer scheme for handling front-end         *
16448 * event. This code as been requested for allowing contemporary       *
16449 * task handling a)acquisition, b)network transfer if possible.       *
16450 * The pre-compiler switch will determine the mode of operation.      *
16451 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
16452 * a seperate task taking care of the dm_area_send                    *
16453 *                                                                    *
16454 * "*" : visible functions                                            *
16455 * dm_buffer_create():     *Setup the dual memory buffer              *
16456 *                          Setup semaphore                           *
16457 *                          Spawn second thread                       *
16458 * dm_buffer_release():    *Release memory allocation for dm          *
16459 *                          Force a kill of 2nd thread                *
16460 *                          Remove semaphore                          *
16461 * dm_area_full():         *Check for both area being full            *
16462 *                          None blocking, may be used for interrupt  *
16463 *                          disable.                                  *
16464 * dm_pointer_get()     :  *Check memory space and return pointer     *
16465 *                          Blocking function with timeout if no more *
16466 *                          space for next event. If error will abort.*
16467 * dm_pointer_increment(): *Move pointer to next free location        *
16468 *                          None blocking. performs bm_send_event if  *
16469 *                          local connection.                         *
16470 * dm_area_send():         *Transfer FULL buffer(s)                   *
16471 *                          None blocking function.                   *
16472 *                          if DUAL_THREAD: Give sem_send semaphore   *
16473 *                          else transfer FULL buffer                 *
16474 * dm_area_flush():        *Transfer all remaining events from dm     *
16475 *                          Blocking function with timeout            *
16476 *                          if DUAL_THREAD: Give sem_flush semaphore. *
16477 * dm_task():               Secondary thread handling DUAL_THREAD     *
16478 *                          mechanism. Serves 2 requests:             *
16479 *                          dm_send:  Transfer FULL buffer only.      *
16480 *                          dm_flush: Transfer ALL buffers.           *
16481 * dm_area_switch():        internal, used by dm_pointer_get()        *
16482 * dm_active_full():        internal: check space in current buffer   *
16483 * dm_buffer_send():        internal: send data for given area        *
16484 * dm_buffer_time_get():    interal: return the time stamp of the     *
16485 *                          last switch                               *
16486 \********************************************************************/
16487 
16488 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
16489 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
16490 #define DM_KILL        12       /* Kill request for 2nd thread */
16491 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
16492 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
16493 
16494 typedef struct {
16495    char *pt;                    /* top pointer    memory buffer          */
16496    char *pw;                    /* write pointer  memory buffer          */
16497    char *pe;                    /* end   pointer  memory buffer          */
16498    char *pb;                    /* bottom pointer memory buffer          */
16499    BOOL full;                   /* TRUE if memory buffer is full         */
16500    DWORD serial;                /* full buffer serial# for evt order     */
16501 } DMEM_AREA;
16502 
16503 typedef struct {
16504    DMEM_AREA *pa;               /* active memory buffer */
16505    DMEM_AREA area1;             /* mem buffer area 1 */
16506    DMEM_AREA area2;             /* mem buffer area 2 */
16507    DWORD serial;                /* overall buffer serial# for evt order     */
16508    INT action;                  /* for multi thread configuration */
16509    DWORD last_active;           /* switch time stamp */
16510    HNDLE sem_send;              /* semaphore for dm_task */
16511    HNDLE sem_flush;             /* semaphore for dm_task */
16512 } DMEM_BUFFER;
16513 
16514 DMEM_BUFFER dm;
16515 INT dm_user_max_event_size;
16516 
16517 /**dox***************************************************************/
16518 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16519 
16520 /********************************************************************/
16521 /**
16522 Setup a dual memory buffer. Has to be called initially before
16523            any other dm_xxx function
16524 @param size             Size in bytes
16525 @param user_max_event_size max event size
16526 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
16527 */
16528 INT dm_buffer_create(INT size, INT user_max_event_size)
16529 {
16530 
16531    dm.area1.pt = (char *) M_MALLOC(size);
16532    if (dm.area1.pt == NULL)
16533       return (BM_NO_MEMORY);
16534    dm.area2.pt = (char *) M_MALLOC(size);
16535    if (dm.area2.pt == NULL)
16536       return (BM_NO_MEMORY);
16537 
16538    /* check user event size against the system MAX_EVENT_SIZE */
16539    if (user_max_event_size > MAX_EVENT_SIZE) {
16540       cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
16541       return BM_MEMSIZE_MISMATCH;
16542    }
16543    dm_user_max_event_size = user_max_event_size;
16544 
16545    memset(dm.area1.pt, 0, size);
16546    memset(dm.area2.pt, 0, size);
16547 
16548    /* initialize pointers */
16549    dm.area1.pb = dm.area1.pt + size - 1024;
16550    dm.area1.pw = dm.area1.pe = dm.area1.pt;
16551    dm.area2.pb = dm.area2.pt + size - 1024;
16552    dm.area2.pw = dm.area2.pe = dm.area2.pt;
16553 
16554   /*-PAA-*/
16555 #ifdef DM_DEBUG
16556    printf(" in dm_buffer_create ---------------------------------\n");
16557    printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw, dm.area1.pe, dm.area1.pb);
16558    printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw, dm.area2.pe, dm.area2.pb);
16559 #endif
16560 
16561    /* activate first area */
16562    dm.pa = &dm.area1;
16563 
16564    /* Default not full */
16565    dm.area1.full = dm.area2.full = FALSE;
16566 
16567    /* Reset serial buffer number with proper starting sequence */
16568    dm.area1.serial = dm.area2.serial = 0;
16569    /* ensure proper serial on next increment */
16570    dm.serial = 1;
16571 
16572    /* set active buffer time stamp */
16573    dm.last_active = ss_millitime();
16574 
16575    /* get socket for event sending */
16576    _send_sock = rpc_get_event_sock();
16577 
16578 #ifdef DM_DUAL_THREAD
16579    {
16580       INT status;
16581       VX_TASK_SPAWN starg;
16582 
16583       /* create semaphore */
16584       status = ss_mutex_create("send", &dm.sem_send);
16585       if (status != SS_CREATED && status != SS_SUCCESS) {
16586          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
16587          return status;
16588       }
16589       status = ss_mutex_create("flush", &dm.sem_flush);
16590       if (status != SS_CREATED && status != SS_SUCCESS) {
16591          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
16592          return status;
16593       }
16594       /* spawn dm_task */
16595       memset(&starg, 0, sizeof(VX_TASK_SPAWN));
16596 
16597 #ifdef OS_VXWORKS
16598       /* Fill up the necessary arguments */
16599       strcpy(starg.name, "areaSend");
16600       starg.priority = 120;
16601       starg.stackSize = 20000;
16602 #endif
16603 
16604       if ((status = ss_thread_create(dm_task, (void *) &starg))
16605           != SS_SUCCESS) {
16606          cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
16607          return status;
16608       }
16609 #ifdef OS_WINNT
16610       /* necessary for true MUTEX (NT) */
16611       ss_mutex_wait_for(dm.sem_send, 0);
16612 #endif
16613    }
16614 #endif                          /* DM_DUAL_THREAD */
16615 
16616    return CM_SUCCESS;
16617 }
16618 
16619 /**dox***************************************************************/
16620 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16621 
16622 /********************************************************************/
16623 INT dm_buffer_release(void)
16624 /********************************************************************\
16625   Routine: dm_buffer_release
16626 
16627   Purpose: Release dual memory buffers
16628   Input:
16629     none
16630   Output:
16631     none
16632   Function value:
16633     CM_SUCCESS              Successful completion
16634 \********************************************************************/
16635 {
16636    if (dm.area1.pt) {
16637       free(dm.area1.pt);
16638       dm.area1.pt = NULL;
16639    }
16640    if (dm.area2.pt) {
16641       free(dm.area2.pt);
16642       dm.area2.pt = NULL;
16643    }
16644    dm.serial = 0;
16645    dm.area1.full = dm.area2.full = TRUE;
16646    dm.area1.serial = dm.area2.serial = 0;
16647 
16648 #ifdef DM_DUAL_THREAD
16649    /* kill spawned dm_task */
16650    dm.action = DM_KILL;
16651    ss_mutex_release(dm.sem_send);
16652    ss_mutex_release(dm.sem_flush);
16653 
16654    /* release semaphore */
16655    ss_mutex_delete(dm.sem_send, 0);
16656    ss_mutex_delete(dm.sem_flush, 0);
16657 #endif
16658 
16659    return CM_SUCCESS;
16660 }
16661 
16662 /********************************************************************/
16663 INLINE DMEM_AREA *dm_area_switch(void)
16664 /********************************************************************\
16665   Routine: dm_area_switch
16666 
16667   Purpose: set active area to the other empty area or NULL if both
16668            area are full. May have to check the serial consistancy...
16669   Input:
16670     none
16671   Output:
16672     none
16673   Function value:
16674     DMEM_AREA *            Pointer to active area or both full
16675 \********************************************************************/
16676 {
16677    volatile BOOL full1, full2;
16678 
16679    full1 = dm.area1.full;
16680    full2 = dm.area2.full;
16681 
16682    if (!full1 && !full2) {
16683       if (dm.area1.serial <= dm.area2.serial)
16684          return (&(dm.area1));
16685       else
16686          return (&(dm.area2));
16687    }
16688 
16689    if (!full1) {
16690       return (&(dm.area1));
16691    } else if (!full2) {
16692       return (&(dm.area2));
16693    }
16694    return (NULL);
16695 }
16696 
16697 /********************************************************************/
16698 INLINE BOOL dm_area_full(void)
16699 /********************************************************************\
16700   Routine: dm_area_full
16701 
16702   Purpose: Test if both area are full in order to block interrupt
16703   Input:
16704     none
16705   Output:
16706     none
16707   Function value:
16708     BOOL         TRUE if not enough space for another event
16709 \********************************************************************/
16710 {
16711    if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
16712       return TRUE;
16713    return FALSE;
16714 }
16715 
16716 /********************************************************************/
16717 INLINE BOOL dm_active_full(void)
16718 /********************************************************************\
16719   Routine: dm_active_full
16720 
16721   Purpose: Test if there is sufficient space in either event buffer
16722            for another event.
16723   Input:
16724     none
16725   Output:
16726     none
16727   Function value:
16728     BOOL         TRUE if not enough space for another event
16729 \********************************************************************/
16730 {
16731    /* catch both full areas, waiting for transfer */
16732    if (dm.pa == NULL)
16733       return TRUE;
16734    /* Check the space in the active buffer only
16735       as I don't switch buffer here */
16736    if (dm.pa->full)
16737       return TRUE;
16738    return (((POINTER_T) dm.pa->pb - (POINTER_T) dm.pa->pw) < (INT)
16739            (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
16740 }
16741 
16742 /********************************************************************/
16743 DWORD dm_buffer_time_get(void)
16744 /********************************************************************\
16745   Routine: dm_buffer_time_get
16746 
16747   Purpose: return the time from the last buffer switch.
16748 
16749   Input:
16750     none
16751   Output:
16752     none
16753   Function value:
16754     DWORD        time stamp
16755 
16756 \********************************************************************/
16757 {
16758    return (dm.last_active);
16759 }
16760 
16761 
16762 /********************************************************************/
16763 EVENT_HEADER *dm_pointer_get(void)
16764 /********************************************************************\
16765   Routine: dm_pointer_get
16766 
16767   Purpose: Get pointer to next free location in event buffer.
16768            after 10sec tries, it times out return NULL indicating a
16769            serious problem, i.e. abort.
16770   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
16771   Input:
16772     none
16773   Output:
16774     DM_BUFFER * dm    local valid dm to work on
16775   Function value:
16776     EVENT_HEADER *    Pointer to free location
16777     NULL              cannot after several attempt get free space => abort
16778 \********************************************************************/
16779 {
16780    int timeout, status;
16781 
16782    /* Is there still space in the active area ? */
16783    if (!dm_active_full())
16784       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
16785 
16786    /* no more space => switch area */
16787 
16788    /* Tag current area with global dm.serial for order consistency */
16789    dm.pa->serial = dm.serial++;
16790 
16791    /* set active buffer time stamp */
16792    dm.last_active = ss_millitime();
16793 
16794    /* mark current area full */
16795    dm.pa->full = TRUE;
16796 
16797    /* Trigger/do data transfer (Now/don't wait) */
16798    if ((status = dm_area_send()) == RPC_NET_ERROR) {
16799       cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
16800       return NULL;
16801    }
16802 
16803    /* wait switch completion (max 10 sec) */
16804    timeout = ss_millitime();    /* with timeout */
16805    while ((ss_millitime() - timeout) < 10000) {
16806       dm.pa = dm_area_switch();
16807       if (dm.pa != NULL)
16808          return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
16809       ss_sleep(200);
16810 #ifdef DM_DEBUG
16811       printf(" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
16812              ss_millitime() - timeout, dm.area1.full, dm.area2.full, dm.area1.serial,
16813              dm.area2.serial, dm.serial);
16814 #endif
16815    }
16816 
16817    /* Time running out abort */
16818    cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
16819    return NULL;
16820 }
16821 
16822 
16823 /********************************************************************/
16824 int dm_pointer_increment(INT buffer_handle, INT event_size)
16825 /********************************************************************\
16826   Routine: dm_pointer_increment
16827 
16828   Purpose: Increment write pointer of event buffer after an event
16829            has been copied into the buffer (at an address previously
16830            obtained via dm_pointer_get)
16831   Input:
16832     INT buffer_handle         Buffer handle event should be sent to
16833     INT event_size            Event size in bytes including header
16834   Output:
16835     none
16836   Function value:
16837     CM_SUCCESS                Successful completion
16838     status                    from bm_send_event for local connection
16839 \********************************************************************/
16840 {
16841    INT aligned_event_size;
16842 
16843    /* if not connected remotely, use bm_send_event */
16844    if (_send_sock == 0) {
16845       *((INT *) dm.pa->pw) = buffer_handle;
16846       return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT), event_size, SYNC);
16847    }
16848    aligned_event_size = ALIGN8(event_size);
16849 
16850    *((INT *) dm.pa->pw) = buffer_handle;
16851 
16852    /* adjust write pointer */
16853    dm.pa->pw += sizeof(INT) + aligned_event_size;
16854 
16855    /* adjust end pointer */
16856    dm.pa->pe = dm.pa->pw;
16857 
16858    return CM_SUCCESS;
16859 }
16860 
16861 /********************************************************************/
16862 INLINE INT dm_buffer_send(DMEM_AREA * larea)
16863 /********************************************************************\
16864   Routine: dm_buffer_send
16865 
16866   Purpose: Ship data to the cache in fact!
16867            Basically the same do loop is done in the send_tcp.
16868            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
16869            Introduced for bringing tcp option to user code.
16870   Input:
16871     DMEM_AREA * larea   The area to work with.
16872   Output:
16873     none
16874   Function value:
16875     CM_SUCCESS       Successful completion
16876     DM_ACTIVE_NULL   Both area were/are full
16877     RPC_NET_ERROR    send error
16878 \********************************************************************/
16879 {
16880    INT tot_size, nwrite;
16881    char *lpt;
16882 
16883    /* if not connected remotely, use bm_send_event */
16884    if (_send_sock == 0)
16885       return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
16886 
16887    /* alias */
16888    lpt = larea->pt;
16889 
16890    /* Get overall buffer size */
16891    tot_size = (POINTER_T) larea->pe - (POINTER_T) lpt;
16892 
16893    /* shortcut for speed */
16894    if (tot_size == 0)
16895       return CM_SUCCESS;
16896 
16897 #ifdef DM_DEBUG
16898    printf("lpt:%p size:%i ", lpt, tot_size);
16899 #endif
16900    nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
16901 #ifdef DM_DEBUG
16902    printf("nwrite:%i  errno:%i\n", nwrite, errno);
16903 #endif
16904    if (nwrite < 0)
16905       return RPC_NET_ERROR;
16906 
16907    /* reset area */
16908    larea->pw = larea->pe = larea->pt;
16909    larea->full = FALSE;
16910    return CM_SUCCESS;
16911 }
16912 
16913 /********************************************************************/
16914 INT dm_area_send(void)
16915 /********************************************************************\
16916   Routine: dm_area_send
16917 
16918   Purpose: Empty the FULL area only in proper event order
16919            Meant to be use either in mfe.c scheduler on every event
16920 
16921   Dual memory scheme:
16922    DM_DUAL_THREAD : Trigger sem_send
16923    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
16924                     if dm.pa is NULL (were both full) and now both are empty
16925 
16926   Input:
16927     none
16928   Output:
16929     none
16930   Function value:
16931     CM_SUCCESS                Successful completion
16932     RPC_NET_ERROR             send error
16933 \********************************************************************/
16934 {
16935 #ifdef DM_DUAL_THREAD
16936    INT status;
16937 
16938    /* force a DM_SEND if possible. Don't wait for completion */
16939    dm.action = DM_SEND;
16940    ss_mutex_release(dm.sem_send);
16941 #ifdef OS_WINNT
16942    /* necessary for true MUTEX (NT) */
16943    status = ss_mutex_wait_for(dm.sem_send, 1);
16944    if (status == SS_NO_MUTEX) {
16945       printf(" timeout while waiting for sem_send\n");
16946       return RPC_NET_ERROR;
16947    }
16948 #endif
16949 
16950    return CM_SUCCESS;
16951 #else
16952    /* ---------- NOT IN DUAL THREAD ----------- */
16953    INT status = 0;
16954 
16955    /* if no DUAL thread everything is local then */
16956    /* select the full area */
16957    if (dm.area1.full && dm.area2.full)
16958       if (dm.area1.serial <= dm.area2.serial)
16959          status = dm_buffer_send(&dm.area1);
16960       else
16961          status = dm_buffer_send(&dm.area2);
16962    else if (dm.area1.full)
16963       status = dm_buffer_send(&dm.area1);
16964    else if (dm.area2.full)
16965       status = dm_buffer_send(&dm.area2);
16966    if (status != CM_SUCCESS)
16967       return status;            /* catch transfer error too */
16968 
16969    if (dm.pa == NULL) {
16970       printf(" sync send dm.pa:%p full 1%d 2%d\n", dm.pa, dm.area1.full, dm.area2.full);
16971       dm.pa = &dm.area1;
16972    }
16973    return CM_SUCCESS;
16974 #endif
16975 }
16976 
16977 /********************************************************************/
16978 INT dm_task(void *pointer)
16979 /********************************************************************\
16980   Routine: dm_task
16981 
16982   Purpose: async send events doing a double purpose:
16983   a) send full buffer if found (DM_SEND) set by dm_active_full
16984   b) flush full areas (DM_FLUSH) set by dm_area_flush
16985   Input:
16986   none
16987   Output:
16988   none
16989   Function value:
16990   none
16991   \********************************************************************/
16992 {
16993 #ifdef DM_DUAL_THREAD
16994    INT status, timeout;
16995 
16996    printf("Semaphores initialization ... in areaSend ");
16997    /* Check or Wait for semaphore to be setup */
16998    timeout = ss_millitime();
16999    while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
17000       ss_sleep(200);
17001    if (dm.sem_send == 0)
17002       goto kill;
17003 
17004 #ifdef OS_WINNT
17005    /* necessary for true MUTEX (NT) get semaphore */
17006    ss_mutex_wait_for(dm.sem_flush, 0);
17007 #endif
17008 
17009    /* Main FOREVER LOOP */
17010    printf("task areaSend ready...\n");
17011    while (1) {
17012       if (!dm_area_full()) {
17013          /* wait semaphore here ........ 0 == forever */
17014          ss_mutex_wait_for(dm.sem_send, 0);
17015 #ifdef OS_WINNT
17016          /* necessary for true MUTEX (NT) give semaphore */
17017          ss_mutex_release(dm.sem_send);
17018 #endif
17019       }
17020       if (dm.action == DM_SEND) {
17021 #ifdef DM_DEBUG
17022          printf("Send %i %i ", dm.area1.full, dm.area2.full);
17023 #endif
17024          /* DM_SEND : Empty the oldest buffer only. */
17025          if (dm.area1.full && dm.area2.full) {
17026             if (dm.area1.serial <= dm.area2.serial)
17027                status = dm_buffer_send(&dm.area1);
17028             else
17029                status = dm_buffer_send(&dm.area2);
17030          } else if (dm.area1.full)
17031             status = dm_buffer_send(&dm.area1);
17032          else if (dm.area2.full)
17033             status = dm_buffer_send(&dm.area2);
17034 
17035          if (status != CM_SUCCESS) {
17036             cm_msg(MERROR, "dm_task", "network error %i", status);
17037             goto kill;
17038          }
17039       } /* if DM_SEND */
17040       else if (dm.action == DM_FLUSH) {
17041          /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
17042             events) Empty both area in order independently of being full or not */
17043          if (dm.area1.serial <= dm.area2.serial) {
17044             status = dm_buffer_send(&dm.area1);
17045             if (status != CM_SUCCESS)
17046                goto error;
17047             status = dm_buffer_send(&dm.area2);
17048             if (status != CM_SUCCESS)
17049                goto error;
17050          } else {
17051             status = dm_buffer_send(&dm.area2);
17052             if (status != CM_SUCCESS)
17053                goto error;
17054             status = dm_buffer_send(&dm.area1);
17055             if (status != CM_SUCCESS)
17056                goto error;
17057          }
17058          /* reset counter */
17059          dm.area1.serial = 0;
17060          dm.area2.serial = dm.serial = 1;
17061 #ifdef DM_DEBUG
17062          printf("dm.action: Flushing ...\n");
17063 #endif
17064          /* reset area to #1 */
17065          dm.pa = &dm.area1;
17066 
17067          /* release user */
17068          ss_mutex_release(dm.sem_flush);
17069 #ifdef OS_WINNT
17070          /* necessary for true MUTEX (NT) get semaphore back */
17071          ss_mutex_wait_for(dm.sem_flush, 0);
17072 #endif
17073       }
17074       /* if FLUSH */
17075       if (dm.action == DM_KILL)
17076          goto kill;
17077 
17078    }                            /* FOREVER (go back wainting for semaphore) */
17079 
17080    /* kill spawn now */
17081  error:
17082    cm_msg(MERROR, "dm_area_flush", "aSync Net error");
17083  kill:
17084    ss_mutex_release(dm.sem_flush);
17085 #ifdef OS_WINNT
17086    ss_mutex_wait_for(dm.sem_flush, 1);
17087 #endif
17088    cm_msg(MERROR, "areaSend", "task areaSend exiting now");
17089    exit;
17090    return 1;
17091 #else
17092    printf("DM_DUAL_THREAD not defined\n");
17093    return 0;
17094 #endif
17095 }
17096 
17097 /********************************************************************/
17098 INT dm_area_flush(void)
17099 /********************************************************************\
17100   Routine: dm_area_flush
17101 
17102   Purpose: Flush all the events in the areas.
17103            Used in mfe for BOR events, periodic events and
17104            if rate to low in main loop once a second. The standard
17105            data transfer should be done/triggered by dm_area_send (sync/async)
17106            in dm_pointer_get().
17107   Input:
17108     none
17109   Output:
17110     none
17111   Function value:
17112     CM_SUCCESS       Successful completion
17113     RPC_NET_ERROR    send error
17114 \********************************************************************/
17115 {
17116    INT status;
17117 #ifdef DM_DUAL_THREAD
17118    /* request FULL flush */
17119    dm.action = DM_FLUSH;
17120    ss_mutex_release(dm.sem_send);
17121 #ifdef OS_WINNT
17122    /* necessary for true MUTEX (NT) get semaphore back */
17123    ss_mutex_wait_for(dm.sem_send, 0);
17124 #endif
17125 
17126    /* important to wait for completion before continue with timeout
17127       timeout specified milliseconds */
17128    status = ss_mutex_wait_for(dm.sem_flush, 10000);
17129 #ifdef DM_DEBUG
17130    printf("dm_area_flush after waiting %i\n", status);
17131 #endif
17132 #ifdef OS_WINNT
17133    ss_mutex_release(dm.sem_flush);      /* give it back now */
17134 #endif
17135 
17136    return status;
17137 #else
17138    /* full flush done here */
17139    /* select in order both area independently of being full or not */
17140    if (dm.area1.serial <= dm.area2.serial) {
17141       status = dm_buffer_send(&dm.area1);
17142       if (status != CM_SUCCESS)
17143          return status;
17144       status = dm_buffer_send(&dm.area2);
17145       if (status != CM_SUCCESS)
17146          return status;
17147    } else {
17148       status = dm_buffer_send(&dm.area2);
17149       if (status != CM_SUCCESS)
17150          return status;
17151       status = dm_buffer_send(&dm.area1);
17152       if (status != CM_SUCCESS)
17153          return status;
17154    }
17155    /* reset serial counter */
17156    dm.area1.serial = dm.area2.serial = 0;
17157    dm.last_active = ss_millitime();
17158    return CM_SUCCESS;
17159 #endif
17160 }
17161 
17162 /********************************************************************/
17163 
17164 /**dox***************************************************************/
17165 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17166 
17167 /**dox***************************************************************/
17168                             /** @} *//* end of dmfunctionc */
17169 
17170 /**dox***************************************************************/
17171                             /** @} *//* end of midasincludecode */

Midas DOC Version 1.9.5 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Sergio Ballestrero - 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 - Tamsen Schurman - Andreas Suter - Jan M.Wouters - Piotr Adam Zolnierczuk