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 3648 2007-03-09 06:44:12Z amaudruz $
00009 
00010 \********************************************************************/
00011 
00012 #include "midas.h"
00013 #include "msystem.h"
00014 #include "strlcpy.h"
00015 #include <assert.h>
00016 
00017 /* SVN revision number. This value will be changed by the Subversion
00018    system automatically on every revision of midas.c. It will be
00019    returned by cm_get_revision(), for example in the odbedit "ver"
00020    command */
00021 char *svn_revision = "$Rev: 3648 $";
00022 
00023 /**dox***************************************************************/
00024 /** @file midas.c
00025 The main core C-code for Midas.
00026 */
00027 
00028 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00029  */
00030 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00031  */
00032 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00033  */
00034 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00035  */
00036 /** @defgroup alfunctionc Midas Alarm Functions (al_xxx)
00037  */
00038 /** @defgroup hsfunctionc Midas History Functions (hs_xxx)
00039  */
00040 /** @defgroup elfunctionc Midas Elog Functions (el_xxx)
00041  */
00042 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00043  */
00044 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00045  */
00046 /** @defgroup rbfunctionc Midas Ring Buffer Functions (rb_xxx)
00047  */
00048 
00049 /**dox***************************************************************/
00050 /** @addtogroup midasincludecode
00051  *
00052  *  @{  */
00053 
00054 /**dox***************************************************************/
00055 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00056 
00057 /********************************************************************/
00058 /* data type sizes */
00059 INT tid_size[] = {
00060    0,                           /* tid == 0 not defined                               */
00061    1,                           /* TID_BYTE      unsigned byte         0       255    */
00062    1,                           /* TID_SBYTE     signed byte         -128      127    */
00063    1,                           /* TID_CHAR      single character      0       255    */
00064    2,                           /* TID_WORD      two bytes             0      65535   */
00065    2,                           /* TID_SHORT     signed word        -32768    32767   */
00066    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00067    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00068    4,                           /* TID_BOOL      four bytes bool       0        1     */
00069    4,                           /* TID_FLOAT     4 Byte float format                  */
00070    8,                           /* TID_DOUBLE    8 Byte float format                  */
00071    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00072    0,                           /* TID_STRING    zero terminated string               */
00073    0,                           /* TID_ARRAY     variable length array of unkown type */
00074    0,                           /* TID_STRUCT    C structure                          */
00075    0,                           /* TID_KEY       key in online database               */
00076    0                            /* TID_LINK      link in online database              */
00077 };
00078 
00079 /* data type names */
00080 char *tid_name[] = {
00081    "NULL",
00082    "BYTE",
00083    "SBYTE",
00084    "CHAR",
00085    "WORD",
00086    "SHORT",
00087    "DWORD",
00088    "INT",
00089    "BOOL",
00090    "FLOAT",
00091    "DOUBLE",
00092    "BITFIELD",
00093    "STRING",
00094    "ARRAY",
00095    "STRUCT",
00096    "KEY",
00097    "LINK"
00098 };
00099 
00100 struct {
00101    int transition;
00102    char name[32];
00103 } trans_name[] = {
00104    {
00105    TR_START, "START",}, {
00106    TR_STOP, "STOP",}, {
00107    TR_PAUSE, "PAUSE",}, {
00108    TR_RESUME, "RESUME",}, {
00109    TR_DEFERRED, "DEFERRED",}, {
00110 0, "",},};
00111 
00112 /* Globals */
00113 #ifdef OS_MSDOS
00114 extern unsigned _stklen = 60000U;
00115 #endif
00116 
00117 extern DATABASE *_database;
00118 extern INT _database_entries;
00119 
00120 static BUFFER *_buffer;
00121 static INT _buffer_entries = 0;
00122 
00123 static INT _msg_buffer = 0;
00124 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00125 
00126 static REQUEST_LIST *_request_list;
00127 static INT _request_list_entries = 0;
00128 
00129 static EVENT_HEADER *_event_buffer;
00130 static INT _event_buffer_size = 0;
00131 
00132 static char *_net_recv_buffer;
00133 static INT _net_recv_buffer_size = 0;
00134 
00135 static char *_net_send_buffer;
00136 static INT _net_send_buffer_size = 0;
00137 
00138 static char *_tcp_buffer = NULL;
00139 static INT _tcp_wp = 0;
00140 static INT _tcp_rp = 0;
00141 static INT _rpc_sock = 0;
00142 
00143 static INT _send_sock;
00144 
00145 static void (*_debug_print) (char *) = NULL;
00146 static INT _debug_mode = 0;
00147 
00148 static INT _watchdog_last_called = 0;
00149 
00150 /* table for transition functions */
00151 
00152 typedef struct {
00153    INT transition;
00154    INT sequence_number;
00155     INT(*func) (INT, char *);
00156 } TRANS_TABLE;
00157 
00158 #define MAX_TRANSITIONS 20
00159 
00160 TRANS_TABLE _trans_table[MAX_TRANSITIONS];
00161 
00162 TRANS_TABLE _deferred_trans_table[] = {
00163    {TR_START},
00164    {TR_STOP},
00165    {TR_PAUSE},
00166    {TR_RESUME},
00167    {0}
00168 };
00169 
00170 static BOOL _server_registered = FALSE;
00171 
00172 static INT rpc_transition_dispatch(INT index, void *prpc_param[]);
00173 
00174 void cm_ctrlc_handler(int sig);
00175 
00176 typedef struct {
00177    INT code;
00178    char *string;
00179 } ERROR_TABLE;
00180 
00181 ERROR_TABLE _error_table[] = {
00182    {CM_WRONG_PASSWORD, "Wrong password"},
00183    {CM_UNDEF_EXP, "Experiment not defined"},
00184    {CM_UNDEF_ENVIRON,
00185     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00186    {RPC_NET_ERROR, "Cannot connect to remote host"},
00187    {0, NULL}
00188 };
00189 
00190 
00191 typedef struct {
00192    void *adr;
00193    int size;
00194    char file[80];
00195    int line;
00196 } DBG_MEM_LOC;
00197 
00198 DBG_MEM_LOC *_mem_loc = NULL;
00199 INT _n_mem = 0;
00200 
00201 void *dbg_malloc(unsigned int size, char *file, int line)
00202 {
00203    FILE *f;
00204    void *adr;
00205    int i;
00206 
00207    adr = malloc(size);
00208 
00209    /* search for deleted entry */
00210    for (i = 0; i < _n_mem; i++)
00211       if (_mem_loc[i].adr == NULL)
00212          break;
00213 
00214    if (i == _n_mem) {
00215       _n_mem++;
00216       if (!_mem_loc)
00217          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00218       else
00219          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00220    }
00221 
00222    _mem_loc[i].adr = adr;
00223    _mem_loc[i].size = size;
00224    strcpy(_mem_loc[i].file, file);
00225    _mem_loc[i].line = line;
00226 
00227    f = fopen("mem.txt", "w");
00228    for (i = 0; i < _n_mem; i++)
00229       if (_mem_loc[i].adr)
00230          fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
00231                  _mem_loc[i].size, _mem_loc[i].adr);
00232    fclose(f);
00233 
00234    return adr;
00235 }
00236 
00237 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00238 {
00239    void *adr;
00240 
00241    adr = dbg_malloc(size * count, file, line);
00242    if (adr)
00243       memset(adr, 0, size * count);
00244 
00245    return adr;
00246 }
00247 
00248 void dbg_free(void *adr, char *file, int line)
00249 {
00250    FILE *f;
00251    int i;
00252 
00253    free(adr);
00254 
00255    for (i = 0; i < _n_mem; i++)
00256       if (_mem_loc[i].adr == adr)
00257          break;
00258 
00259    if (i < _n_mem)
00260       _mem_loc[i].adr = NULL;
00261 
00262    f = fopen("mem.txt", "w");
00263    for (i = 0; i < _n_mem; i++)
00264       if (_mem_loc[i].adr)
00265          fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
00266                  _mem_loc[i].size, _mem_loc[i].adr);
00267    fclose(f);
00268 }
00269 
00270 /********************************************************************\
00271 *                                                                    *
00272 *              Common message functions                              *
00273 *                                                                    *
00274 \********************************************************************/
00275 
00276 static int (*_message_print) (const char *) = puts;
00277 static INT _message_mask_system = MT_ALL;
00278 static INT _message_mask_user = MT_ALL;
00279 
00280 
00281 /**dox***************************************************************/
00282 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00283 
00284 /**dox***************************************************************/
00285 /** @addtogroup msgfunctionc
00286  *
00287  *  @{  */
00288 
00289 /********************************************************************/
00290 /**
00291 Convert error code to string. Used after cm_connect_experiment to print
00292 error string in command line programs or windows programs.
00293 @param code Error code as defined in midas.h
00294 @param string Error string
00295 @return CM_SUCCESS
00296 */
00297 INT cm_get_error(INT code, char *string)
00298 {
00299    INT i;
00300 
00301    for (i = 0; _error_table[i].code; i++)
00302       if (_error_table[i].code == code) {
00303          strcpy(string, _error_table[i].string);
00304          return CM_SUCCESS;
00305       }
00306 
00307    sprintf(string, "Unexpected error #%d", code);
00308    return CM_SUCCESS;
00309 }
00310 
00311 /********************************************************************/
00312 /**
00313 Set message masks. When a message is generated by calling cm_msg(),
00314 it can got to two destinatinons. First a user defined callback routine
00315 and second to the "SYSMSG" buffer.
00316 
00317 A user defined callback receives all messages which satisfy the user_mask.
00318 
00319 \code
00320 int message_print(const char *msg)
00321 {
00322   char str[160];
00323 
00324   memset(str, ' ', 159);
00325   str[159] = 0;
00326   if (msg[0] == '[')
00327     msg = strchr(msg, ']')+2;
00328   memcpy(str, msg, strlen(msg));
00329   ss_printf(0, 20, str);
00330   return 0;
00331 }
00332 ...
00333   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
00334 ...
00335 \endcode
00336 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
00337 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
00338 @param func Function which receives all printout. By setting "puts",
00339        messages are just printed to the screen.
00340 @return CM_SUCCESS
00341 */
00342 INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func) (const char *))
00343 {
00344    _message_mask_system = system_mask;
00345    _message_mask_user = user_mask;
00346    _message_print = func;
00347 
00348    return BM_SUCCESS;
00349 }
00350 
00351 /********************************************************************/
00352 /**
00353 Write message to logging file. Called by cm_msg.
00354 @attention May burn your fingers
00355 @param message_type      Message type
00356 @param message          Message string
00357 @return CM_SUCCESS
00358 */
00359 INT cm_msg_log(INT message_type, const char *message)
00360 {
00361    char dir[256];
00362    char filename[256];
00363    char path[256];
00364    char str[256];
00365    INT status, size, fh;
00366    HNDLE hDB, hKey;
00367 
00368    if (rpc_is_remote())
00369       return rpc_call(RPC_CM_MSG_LOG, message_type, message);
00370 
00371    if (message_type != MT_DEBUG) {
00372       cm_get_experiment_database(&hDB, NULL);
00373 
00374       if (hDB) {
00375          strcpy(filename, "midas.log");
00376          size = sizeof(filename);
00377          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00378 
00379          if (strchr(filename, DIR_SEPARATOR) == NULL) {
00380             status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00381             if (status == DB_SUCCESS) {
00382                size = sizeof(dir);
00383                memset(dir, 0, size);
00384                db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00385                if (dir[0] != 0)
00386                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00387                      strcat(dir, DIR_SEPARATOR_STR);
00388 
00389                strcpy(path, dir);
00390                strcat(path, filename);
00391             } else {
00392                cm_get_path(dir);
00393                if (dir[0] != 0)
00394                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00395                      strcat(dir, DIR_SEPARATOR_STR);
00396 
00397                strcpy(path, dir);
00398                strcat(path, "midas.log");
00399             }
00400          } else {
00401             strcpy(path, filename);
00402          }
00403       } else
00404          strcpy(path, "midas.log");
00405 
00406       fh = open(path, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
00407       if (fh < 0) {
00408          printf("Cannot open message log file %s\n", path);
00409       } else {
00410          strcpy(str, ss_asctime());
00411          write(fh, str, strlen(str));
00412          write(fh, " ", 1);
00413          write(fh, message, strlen(message));
00414          write(fh, "\n", 1);
00415          close(fh);
00416       }
00417    }
00418 
00419    return CM_SUCCESS;
00420 }
00421 
00422 /********************************************************************/
00423 /**
00424 Write message to logging file. Called by cm_msg().
00425 @internal
00426 @param message_type      Message type
00427 @param message          Message string
00428 @param facility         Message facility, filename in which messages will be written
00429 @return CM_SUCCESS
00430 */
00431 INT cm_msg_log1(INT message_type, const char *message, const char *facility)
00432 /********************************************************************\
00433 
00434   Routine: cm_msg_log1
00435 
00436   Purpose: Write message to logging file. Called by cm_msg.
00437            Internal use only
00438 
00439   Input:
00440     INT    message_type      Message type
00441     char   *message          Message string
00442     char   *
00443 
00444   Output:
00445     none
00446 
00447   Function value:
00448     CM_SUCCESS
00449 
00450 \********************************************************************/
00451 {
00452    char dir[256];
00453    char filename[256];
00454    char path[256];
00455    char str[256];
00456    FILE *f;
00457    INT status, size;
00458    HNDLE hDB, hKey;
00459 
00460 
00461    if (rpc_is_remote())
00462       return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
00463 
00464    if (message_type != MT_DEBUG) {
00465       cm_get_experiment_database(&hDB, NULL);
00466 
00467       if (hDB) {
00468          strcpy(filename, "midas.log");
00469          size = sizeof(filename);
00470          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00471 
00472          if (strchr(filename, DIR_SEPARATOR) == NULL) {
00473 
00474             status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00475             if (status == DB_SUCCESS) {
00476                size = sizeof(dir);
00477                memset(dir, 0, size);
00478                db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00479                if (dir[0] != 0)
00480                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00481                      strcat(dir, DIR_SEPARATOR_STR);
00482 
00483                if (facility[0]) {
00484                   strcpy(filename, facility);
00485                   strcat(filename, ".log");
00486                } else {
00487                   strcpy(filename, "midas.log");
00488                   size = sizeof(filename);
00489                   db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00490                }
00491 
00492                strcpy(path, dir);
00493                strcat(path, filename);
00494             } else {
00495                cm_get_path(dir);
00496                if (dir[0] != 0)
00497                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00498                      strcat(dir, DIR_SEPARATOR_STR);
00499 
00500                strcpy(path, dir);
00501                if (facility[0]) {
00502                   strcat(path, facility);
00503                   strcat(path, ".log");
00504                } else
00505                   strcat(path, "midas.log");
00506             }
00507          } else {
00508             strcpy(path, filename);
00509             *(strrchr(path, DIR_SEPARATOR) + 1) = 0;
00510             if (facility[0]) {
00511                strcat(path, facility);
00512                strcat(path, ".log");
00513             } else
00514                strcat(path, "midas.log");
00515          }
00516       } else {
00517          if (facility[0]) {
00518             strcpy(path, facility);
00519             strcat(path, ".log");
00520          } else
00521             strcpy(path, "midas.log");
00522       }
00523 
00524       f = fopen(path, "a");
00525       if (f == NULL) {
00526          printf("Cannot open message log file %s\n", path);
00527       } else {
00528          strcpy(str, ss_asctime());
00529          fprintf(f, str);
00530          fprintf(f, " %s\n", message);
00531          fclose(f);
00532       }
00533    }
00534 
00535    return CM_SUCCESS;
00536 }
00537 
00538 /********************************************************************/
00539 /**
00540 This routine can be called whenever an internal error occurs
00541 or an informative message is produced. Different message
00542 types can be enabled or disabled by setting the type bits
00543 via cm_set_msg_print().
00544 @attention Do not add the "\n" escape carriage control at the end of the
00545 formated line as it is already added by the client on the receiving side.
00546 \code
00547    ...
00548    cm_msg(MINFO, "my program", "This is a information message only);
00549    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
00550    cm_msg(MTALK, "my_program", My program is Done!");
00551    ...
00552 \endcode
00553 @param message_type (See @ref midas_macro).
00554 @param filename Name of source file where error occured
00555 @param line Line number where error occured
00556 @param routine Routine name.
00557 @param format message to printout, ... Parameters like for printf()
00558 @return CM_SUCCESS
00559 */
00560 INT cm_msg(INT message_type, char *filename, INT line, const char *routine, const char *format, ...)
00561 {
00562    va_list argptr;
00563    char event[1000], str[1000], format_cpy[900], local_message[1000], send_message[1000], *pc;
00564    EVENT_HEADER *pevent;
00565    INT status;
00566    static BOOL in_routine = FALSE;
00567 
00568    /* avoid recursive calls */
00569    if (in_routine)
00570       return 0;
00571 
00572    in_routine = TRUE;
00573 
00574    /* strip path */
00575    pc = filename + strlen(filename);
00576    while (*pc != '\\' && *pc != '/' && pc != filename)
00577       pc--;
00578    if (pc != filename)
00579       pc++;
00580 
00581    /* print client name into string */
00582    if (message_type == MT_USER)
00583       sprintf(send_message, "[%s] ", routine);
00584    else {
00585       rpc_get_name(str);
00586       if (str[0])
00587          sprintf(send_message, "[%s] ", str);
00588       else
00589          send_message[0] = 0;
00590    }
00591 
00592    local_message[0] = 0;
00593 
00594    /* preceed error messages with file and line info */
00595    if (message_type == MT_ERROR) {
00596       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
00597       strlcat(send_message, str, sizeof(send_message));
00598       strlcat(local_message, str, sizeof(send_message));
00599    } else if (message_type == MT_USER)
00600       sprintf(local_message, "[%s] ", routine);
00601 
00602    /* limit length of format */
00603    strlcpy(format_cpy, format, sizeof(format_cpy));
00604 
00605    /* print argument list into message */
00606    va_start(argptr, format);
00607    vsprintf(str, (char *) format, argptr);
00608    va_end(argptr);
00609    strcat(send_message, str);
00610    strcat(local_message, str);
00611 
00612    /* call user function if set via cm_set_msg_print */
00613    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00614       _message_print(local_message);
00615 
00616    /* return if system mask is not set */
00617    if ((message_type & _message_mask_system) == 0) {
00618       in_routine = FALSE;
00619       return CM_SUCCESS;
00620    }
00621 
00622    /* copy message to event */
00623    pevent = (EVENT_HEADER *) event;
00624    strcpy(event + sizeof(EVENT_HEADER), send_message);
00625 
00626    /* send event if not of type MLOG */
00627    if (message_type != MT_LOG) {
00628       /* if no message buffer already opened, do so now */
00629       if (_msg_buffer == 0) {
00630          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00631          if (status != BM_SUCCESS && status != BM_CREATED) {
00632             in_routine = FALSE;
00633             return status;
00634          }
00635       }
00636 
00637       /* setup the event header and send the message */
00638       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00639                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00640       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00641    }
00642 
00643    /* log message */
00644    cm_msg_log(message_type, send_message);
00645 
00646    in_routine = FALSE;
00647 
00648    return CM_SUCCESS;
00649 }
00650 
00651 /********************************************************************/
00652 /**
00653 This routine is similar to @ref cm_msg().
00654 It differs from cm_msg() only by the logging destination being a file
00655 given through the argument list i.e:\b facility
00656 @internal
00657 @attention Do not add the "\n" escape carriage control at the end of the
00658 formated line as it is already added by the client on the receiving side.
00659 The first arg in the following example uses the predefined
00660 macro MINFO which handles automatically the first 3 arguments of the function
00661 (see @ref midas_macro).
00662 \code   ...
00663    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
00664    ...
00665 //----- File my_log_file.log
00666 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
00667 \endcode
00668 @param message_type See @ref midas_macro.
00669 @param filename Name of source file where error occured
00670 @param line Line number where error occured
00671 @param facility Logging file name
00672 @param routine Routine name
00673 @param format message to printout, ... Parameters like for printf()
00674 @return CM_SUCCESS
00675 */
00676 INT cm_msg1(INT message_type, char *filename, INT line,
00677             const char *facility, const char *routine, const char *format, ...)
00678 {
00679    va_list argptr;
00680    char event[1000], str[256], local_message[256], send_message[256], *pc;
00681    EVENT_HEADER *pevent;
00682    INT status;
00683    static BOOL in_routine = FALSE;
00684 
00685    /* avoid recursive calles */
00686    if (in_routine)
00687       return 0;
00688 
00689    in_routine = TRUE;
00690 
00691    /* strip path */
00692    pc = filename + strlen(filename);
00693    while (*pc != '\\' && *pc != '/' && pc != filename)
00694       pc--;
00695    if (pc != filename)
00696       pc++;
00697 
00698    /* print client name into string */
00699    if (message_type == MT_USER)
00700       sprintf(send_message, "[%s] ", routine);
00701    else {
00702       rpc_get_name(str);
00703       if (str[0])
00704          sprintf(send_message, "[%s] ", str);
00705       else
00706          send_message[0] = 0;
00707    }
00708 
00709    local_message[0] = 0;
00710 
00711    /* preceed error messages with file and line info */
00712    if (message_type == MT_ERROR) {
00713       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
00714       strcat(send_message, str);
00715       strcat(local_message, str);
00716    }
00717 
00718    /* print argument list into message */
00719    va_start(argptr, format);
00720    vsprintf(str, (char *) format, argptr);
00721    va_end(argptr);
00722 
00723    if (facility)
00724       sprintf(local_message + strlen(local_message), "{%s} ", facility);
00725 
00726    strcat(send_message, str);
00727    strcat(local_message, str);
00728 
00729    /* call user function if set via cm_set_msg_print */
00730    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00731       _message_print(local_message);
00732 
00733    /* return if system mask is not set */
00734    if ((message_type & _message_mask_system) == 0) {
00735       in_routine = FALSE;
00736       return CM_SUCCESS;
00737    }
00738 
00739    /* copy message to event */
00740    pevent = (EVENT_HEADER *) event;
00741    strcpy(event + sizeof(EVENT_HEADER), send_message);
00742 
00743    /* send event if not of type MLOG */
00744    if (message_type != MT_LOG) {
00745       /* if no message buffer already opened, do so now */
00746       if (_msg_buffer == 0) {
00747          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00748          if (status != BM_SUCCESS && status != BM_CREATED) {
00749             in_routine = FALSE;
00750             return status;
00751          }
00752       }
00753 
00754       /* setup the event header and send the message */
00755       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00756                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00757       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00758    }
00759 
00760    /* log message */
00761    cm_msg_log1(message_type, send_message, facility);
00762 
00763    in_routine = FALSE;
00764 
00765    return CM_SUCCESS;
00766 }
00767 
00768 /********************************************************************/
00769 /**
00770 Register a dispatch function for receiving system messages.
00771 - example code from mlxspeaker.c
00772 \code
00773 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
00774 {
00775   char str[256], *pc, *sp;
00776   // print message
00777   printf("%s\n", (char *)(message));
00778 
00779   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
00780                  ,header->event_id
00781                  ,header->trigger_mask
00782                  ,header->serial_number
00783                  ,header->data_size);
00784   pc = strchr((char *)(message),']')+2;
00785   ...
00786   // skip none talking message
00787   if (header->trigger_mask == MT_TALK ||
00788       header->trigger_mask == MT_USER)
00789    ...
00790 }
00791 
00792 int main(int argc, char *argv[])
00793 {
00794   ...
00795   // now connect to server
00796   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
00797   if (status != CM_SUCCESS)
00798     return 1;
00799   // Register callback for messages
00800   cm_msg_register(receive_message);
00801   ...
00802 }
00803 \endcode
00804 @param func Dispatch function.
00805 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
00806 */
00807 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
00808 {
00809    INT status, id;
00810 
00811    /* if no message buffer already opened, do so now */
00812    if (_msg_buffer == 0) {
00813       status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00814       if (status != BM_SUCCESS && status != BM_CREATED)
00815          return status;
00816    }
00817 
00818    _msg_dispatch = func;
00819 
00820    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_SOME, &id, func);
00821 
00822    return status;
00823 }
00824 
00825 /********************************************************************/
00826 /**
00827 Retrieve old messages from log file
00828 @param  n_message        Number of messages to retrieve
00829 @param  message          buf_size bytes of messages, separated
00830                          by \n characters. The returned number
00831                          of bytes is normally smaller than the
00832                          initial buf_size, since only full
00833                          lines are returned.
00834 @param *buf_size         Size of message buffer to fill
00835 @return CM_SUCCESS
00836 */
00837 INT cm_msg_retrieve(INT n_message, char *message, INT * buf_size)
00838 {
00839    char dir[256];
00840    char filename[256];
00841    char path[256], *p;
00842    FILE *f;
00843    INT status, size, offset, i;
00844    HNDLE hDB, hKey;
00845 
00846 
00847    if (rpc_is_remote())
00848       return rpc_call(RPC_CM_MSG_RETRIEVE, message, buf_size);
00849 
00850    cm_get_experiment_database(&hDB, NULL);
00851 
00852    if (hDB) {
00853       strcpy(filename, "midas.log");
00854       size = sizeof(filename);
00855       db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00856 
00857       if (strchr(filename, DIR_SEPARATOR) == NULL) {
00858          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00859          if (status == DB_SUCCESS) {
00860             size = sizeof(dir);
00861             memset(dir, 0, size);
00862             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00863             if (dir[0] != 0)
00864                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00865                   strcat(dir, DIR_SEPARATOR_STR);
00866 
00867             strcpy(filename, "midas.log");
00868             size = sizeof(filename);
00869             db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00870 
00871             strcpy(path, dir);
00872             strcat(path, filename);
00873          } else {
00874             cm_get_path(dir);
00875             if (dir[0] != 0)
00876                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00877                   strcat(dir, DIR_SEPARATOR_STR);
00878 
00879             strcpy(path, dir);
00880             strcat(path, "midas.log");
00881          }
00882       } else {
00883          strcpy(path, filename);
00884       }
00885    } else
00886       strcpy(path, "midas.log");
00887 
00888    f = fopen(path, "rb");
00889    if (f == NULL) {
00890       sprintf(message, "Cannot open message log file %s\n", path);
00891       *buf_size = strlen(message);
00892       return CM_DB_ERROR;
00893    } else {
00894       /* position buf_size bytes before the EOF */
00895       fseek(f, -(*buf_size - 1), SEEK_END);
00896       offset = ftell(f);
00897       if (offset != 0) {
00898          /* go to end of line */
00899          fgets(message, *buf_size - 1, f);
00900          offset = ftell(f) - offset;
00901          *buf_size -= offset;
00902       }
00903 
00904       memset(message, 0, *buf_size);
00905       fread(message, 1, *buf_size - 1, f);
00906       message[*buf_size - 1] = 0;
00907       fclose(f);
00908 
00909       p = message + (*buf_size - 2);
00910 
00911       /* goto end of buffer */
00912       while (p != message && *p == 0)
00913          p--;
00914 
00915       /* strip line break */
00916       while (p != message && (*p == '\n' || *p == '\r'))
00917          *(p--) = 0;
00918 
00919       /* trim buffer so that last n_messages remain */
00920       for (i = 0; i < n_message; i++) {
00921          while (p != message && *p != '\n')
00922             p--;
00923 
00924          while (p != message && (*p == '\n' || *p == '\r'))
00925             p--;
00926       }
00927       if (p != message) {
00928          p++;
00929          while (*p == '\n' || *p == '\r')
00930             p++;
00931       }
00932 
00933       *buf_size = (*buf_size - 1) - (p - message);
00934 
00935       memmove(message, p, *buf_size);
00936       message[*buf_size] = 0;
00937    }
00938 
00939    return CM_SUCCESS;
00940 }
00941 
00942 /**dox***************************************************************/
00943                                                                                                              /** @} *//* end of msgfunctionc */
00944 
00945 /**dox***************************************************************/
00946 /** @addtogroup cmfunctionc
00947  *
00948  *  @{  */
00949 
00950 /********************************************************************/
00951 /**
00952 Get time from MIDAS server and set local time.
00953 @param    seconds         Time in seconds
00954 @return CM_SUCCESS
00955 */
00956 INT cm_synchronize(DWORD * seconds)
00957 {
00958    INT sec, status;
00959 
00960    /* if connected to server, get time from there */
00961    if (rpc_is_remote()) {
00962       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
00963 
00964       /* set local time */
00965       if (status == CM_SUCCESS)
00966          ss_settime(sec);
00967    }
00968 
00969    /* return time to caller */
00970    if (seconds != NULL) {
00971       *seconds = ss_time();
00972    }
00973 
00974    return CM_SUCCESS;
00975 }
00976 
00977 /********************************************************************/
00978 /**
00979 Get time from MIDAS server and set local time.
00980 @param    str            return time string
00981 @param    buf_size       Maximum size of str
00982 @return   CM_SUCCESS
00983 */
00984 INT cm_asctime(char *str, INT buf_size)
00985 {
00986    /* if connected to server, get time from there */
00987    if (rpc_is_remote())
00988       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
00989 
00990    /* return local time */
00991    strcpy(str, ss_asctime());
00992 
00993    return CM_SUCCESS;
00994 }
00995 
00996 /********************************************************************/
00997 /**
00998 Get time from ss_time on server.
00999 @param    time string
01000 @return   CM_SUCCESS
01001 */
01002 INT cm_time(DWORD * time)
01003 {
01004    /* if connected to server, get time from there */
01005    if (rpc_is_remote())
01006       return rpc_call(RPC_CM_TIME, time);
01007 
01008    /* return local time */
01009    *time = ss_time();
01010 
01011    return CM_SUCCESS;
01012 }
01013 
01014 /**dox***************************************************************/
01015                                                                                                              /** @} *//* end of cmfunctionc */
01016 
01017 /********************************************************************\
01018 *                                                                    *
01019 *           cm_xxx  -  Common Functions to buffer & database         *
01020 *                                                                    *
01021 \********************************************************************/
01022 
01023 /* Globals */
01024 
01025 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
01026 static HNDLE _hDB = 0;          /* Database handle */
01027 static char _client_name[NAME_LENGTH];
01028 static char _path_name[MAX_STRING_LENGTH];
01029 static INT _call_watchdog = TRUE;
01030 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01031 INT _mutex_alarm, _mutex_elog;
01032 
01033 /**dox***************************************************************/
01034 /** @addtogroup cmfunctionc
01035  *
01036  *  @{  */
01037 
01038 /**
01039 Return version number of current MIDAS library as a string
01040 @return version number
01041 */
01042 char *cm_get_version()
01043 {
01044    return MIDAS_VERSION;
01045 }
01046 
01047 /**
01048 Return svn revision number of current MIDAS library as a string
01049 @return revision number
01050 */
01051 int cm_get_revision()
01052 {
01053    return atoi(svn_revision+6);
01054 }
01055 
01056 /********************************************************************/
01057 /**
01058 Set path to actual experiment. This function gets called
01059 by cm_connect_experiment if the connection is established
01060 to a local experiment (not through the TCP/IP server).
01061 The path is then used for all shared memory routines.
01062 @param  path             Pathname
01063 @return CM_SUCCESS
01064 */
01065 INT cm_set_path(char *path)
01066 {
01067    strcpy(_path_name, path);
01068 
01069    /* check for trailing directory seperator */
01070    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01071       strcat(_path_name, DIR_SEPARATOR_STR);
01072 
01073    return CM_SUCCESS;
01074 }
01075 
01076 /********************************************************************/
01077 /**
01078 Return the path name previously set with cm_set_path.
01079 @param  path             Pathname
01080 @return CM_SUCCESS
01081 */
01082 INT cm_get_path(char *path)
01083 {
01084    strcpy(path, _path_name);
01085 
01086    return CM_SUCCESS;
01087 }
01088 
01089 /**dox***************************************************************/
01090                                                                                                              /** @} *//* end of cmfunctionc */
01091 
01092 /**dox***************************************************************/
01093 /** @addtogroup cmfunctionc
01094  *
01095  *  @{  */
01096 
01097 /**dox***************************************************************/
01098 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01099 
01100 typedef struct {
01101    char name[NAME_LENGTH];
01102    char directory[MAX_STRING_LENGTH];
01103    char user[NAME_LENGTH];
01104 } experiment_table;
01105 
01106 static experiment_table exptab[MAX_EXPERIMENT];
01107 
01108 /**dox***************************************************************/
01109 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01110 
01111 /**
01112 Scan the "exptab" file for MIDAS experiment names and save them
01113 for later use by rpc_server_accept(). The file is first searched
01114 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01115 @return CM_SUCCESS<br>
01116         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01117 */
01118 INT cm_scan_experiments(void)
01119 {
01120    INT i;
01121    FILE *f;
01122    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01123 
01124    for (i = 0; i < MAX_EXPERIMENT; i++)
01125       exptab[i].name[0] = 0;
01126 
01127    /* MIDAS_DIR overrides exptab */
01128    if (getenv("MIDAS_DIR")) {
01129       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01130 
01131       strcpy(exptab[0].name, "Default");
01132       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01133       exptab[0].user[0] = 0;
01134 
01135       return CM_SUCCESS;
01136    }
01137 
01138    /* default directory for different OSes */
01139 #if defined (OS_WINNT)
01140    if (getenv("SystemRoot"))
01141       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01142    else if (getenv("windir"))
01143       strlcpy(str, getenv("windir"), sizeof(str));
01144    else
01145       strcpy(str, "");
01146 
01147    strcpy(alt_str, str);
01148    strcat(str, "\\system32\\exptab");
01149    strcat(alt_str, "\\system\\exptab");
01150 #elif defined (OS_UNIX)
01151    strcpy(str, "/etc/exptab");
01152    strcpy(alt_str, "/exptab");
01153 #else
01154    strcpy(str, "exptab");
01155    strcpy(alt_str, "exptab");
01156 #endif
01157 
01158    /* MIDAS_EXPTAB overrides default directory */
01159    if (getenv("MIDAS_EXPTAB")) {
01160       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01161       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01162    }
01163 
01164    /* read list of available experiments */
01165    f = fopen(str, "r");
01166    if (f == NULL) {
01167       f = fopen(alt_str, "r");
01168       if (f == NULL)
01169          return CM_UNDEF_ENVIRON;
01170    }
01171 
01172    i = 0;
01173    if (f != NULL) {
01174       do {
01175          str[0] = 0;
01176          if (fgets(str, 100, f) == NULL)
01177             break;
01178          if (str[0] && str[0] != '#') {
01179             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01180 
01181             /* check for trailing directory separator */
01182             pdir = exptab[i].directory;
01183             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01184                strcat(pdir, DIR_SEPARATOR_STR);
01185 
01186             i++;
01187          }
01188       } while (!feof(f));
01189       fclose(f);
01190    }
01191 
01192    /*
01193       for (j=0 ; j<i ; j++)
01194       {
01195       sprintf(str, "Scanned experiment %s", exptab[j].name);
01196       cm_msg(MINFO, str);
01197       }
01198     */
01199 
01200    return CM_SUCCESS;
01201 }
01202 
01203 /********************************************************************/
01204 /**
01205 Delete client info from database
01206 @param hDB               Database handle
01207 @param pid               PID of entry to delete, zero for this process.
01208 @return CM_SUCCESS
01209 */
01210 INT cm_delete_client_info(HNDLE hDB, INT pid)
01211 {
01212 #ifdef LOCAL_ROUTINES
01213 
01214    /* only do it if local */
01215    if (!rpc_is_remote()) {
01216       INT status;
01217       HNDLE hKey;
01218       char str[256];
01219 
01220       if (!pid)
01221          pid = ss_gettid();
01222 
01223       /* don't delete info from a closed database */
01224       if (_database_entries == 0)
01225          return CM_SUCCESS;
01226 
01227       /* make operation atomic by locking database */
01228       db_lock_database(hDB);
01229 
01230       sprintf(str, "System/Clients/%0d", pid);
01231       status = db_find_key1(hDB, 0, str, &hKey);
01232       if (status != DB_SUCCESS) {
01233          db_unlock_database(hDB);
01234          return status;
01235       }
01236 
01237       /* unlock client entry and delete it without locking DB */
01238       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01239       db_delete_key1(hDB, hKey, 1, TRUE);
01240 
01241       db_unlock_database(hDB);
01242 
01243       /* touch notify key to inform others */
01244       status = 0;
01245       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01246    }
01247 #endif                          /*LOCAL_ROUTINES */
01248 
01249    return CM_SUCCESS;
01250 }
01251 
01252 /********************************************************************/
01253 /**
01254 Check if a client with a /system/client/xxx entry has
01255 a valid entry in the ODB client table. If not, remove
01256 that client from the /system/client tree.
01257 @param   hDB               Handle to online database
01258 @param   hKeyClient        Handle to client key
01259 @return  CM_SUCCESS, CM_NO_CLIENT
01260 */
01261 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01262 {
01263 #ifdef LOCAL_ROUTINES
01264 
01265    KEY key;
01266    DATABASE_HEADER *pheader;
01267    DATABASE_CLIENT *pclient;
01268    INT i, client_pid, status;
01269    char name[NAME_LENGTH];
01270 
01271    db_get_key(hDB, hKeyClient, &key);
01272    client_pid = atoi(key.name);
01273 
01274    i = sizeof(name);
01275    db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01276 
01277    db_lock_database(hDB);
01278    if (_database[hDB - 1].attached) {
01279       pheader = _database[hDB - 1].database_header;
01280       pclient = pheader->client;
01281 
01282       /* loop through clients */
01283       for (i = 0; i < pheader->max_client_index; i++, pclient++)
01284          if (pclient->tid == client_pid)
01285             break;
01286 
01287       if (i == pheader->max_client_index) {
01288          /* client not found : delete ODB stucture */
01289          db_unlock_database(hDB);
01290 
01291          status = cm_delete_client_info(hDB, client_pid);
01292          if (status != CM_SUCCESS)
01293             cm_msg(MERROR, "cm_check_client", "cannot delete client info");
01294          else
01295             cm_msg(MINFO, "cm_check_client",
01296                    "Deleted entry \'/System/Clients/%d\' for client \'%s\'\n", client_pid, name);
01297 
01298          return CM_NO_CLIENT;
01299       }
01300    }
01301 
01302    db_unlock_database(hDB);
01303 
01304 #endif                          /*LOCAL_ROUTINES */
01305 
01306    return CM_SUCCESS;
01307 }
01308 
01309 /********************************************************************/
01310 /**
01311 Set client information in online database and return handle
01312 @param  hDB              Handle to online database
01313 @param  hKeyClient       returned key
01314 @param  host_name        server name
01315 @param  client_name      Name of this program as it will be seen
01316                          by other clients.
01317 @param  hw_type          Type of byte order
01318 @param  password         MIDAS password
01319 @param  watchdog_timeout Default watchdog timeout, can be overwritten
01320                          by ODB setting /programs/<name>/Watchdog timeout
01321 @return   CM_SUCCESS
01322 */
01323 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
01324                        char *client_name, INT hw_type, char *password, DWORD watchdog_timeout)
01325 {
01326    if (rpc_is_remote())
01327       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
01328                       host_name, client_name, hw_type, password, watchdog_timeout);
01329 
01330 #ifdef LOCAL_ROUTINES
01331    {
01332       INT status, pid, data, i, index, size;
01333       HNDLE hKey, hSubkey;
01334       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
01335       BOOL call_watchdog, allow;
01336       PROGRAM_INFO_STR(program_info_str);
01337 
01338       /* check security if password is presend */
01339       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
01340       if (hKey) {
01341          /* get password */
01342          size = sizeof(pwd);
01343          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
01344 
01345          /* first check allowed hosts list */
01346          allow = FALSE;
01347          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
01348          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
01349             allow = TRUE;
01350 
01351          /* check allowed programs list */
01352          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
01353          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
01354             allow = TRUE;
01355 
01356          /* now check password */
01357          if (!allow && strcmp(password, pwd) != 0 && strcmp(password, "mid7qBxsNMHux") != 0) {
01358             if (password[0])
01359                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
01360             db_close_all_databases();
01361             bm_close_all_buffers();
01362             _msg_buffer = 0;
01363             return CM_WRONG_PASSWORD;
01364          }
01365       }
01366 
01367       /* make following operation atomic by locking database */
01368       db_lock_database(hDB);
01369 
01370       /* check if entry with this pid exists already */
01371       pid = ss_gettid();
01372 
01373       sprintf(str, "System/Clients/%0d", pid);
01374       status = db_find_key(hDB, 0, str, &hKey);
01375       if (status == DB_SUCCESS) {
01376          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
01377          db_delete_key(hDB, hKey, TRUE);
01378       }
01379 
01380       if (strlen(client_name) >= NAME_LENGTH)
01381          client_name[NAME_LENGTH] = 0;
01382 
01383       strcpy(name, client_name);
01384       strcpy(orig_name, client_name);
01385 
01386       /* check if client name already exists */
01387       status = db_find_key(hDB, 0, "System/Clients", &hKey);
01388 
01389       for (index = 1; status != DB_NO_MORE_SUBKEYS; index++) {
01390          for (i = 0;; i++) {
01391             status = db_enum_key(hDB, hKey, i, &hSubkey);
01392             if (status == DB_NO_MORE_SUBKEYS)
01393                break;
01394 
01395             if (status == DB_SUCCESS) {
01396                size = sizeof(str);
01397                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
01398             }
01399 
01400             /* check if client is living */
01401             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
01402                continue;
01403 
01404             if (equal_ustring(str, name)) {
01405                sprintf(name, "%s%d", client_name, index);
01406                break;
01407             }
01408          }
01409       }
01410 
01411       /* set name */
01412       sprintf(str, "System/Clients/%0d/Name", pid);
01413       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
01414       if (status != DB_SUCCESS) {
01415          db_unlock_database(hDB);
01416          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
01417          return status;
01418       }
01419 
01420       /* copy new client name */
01421       strcpy(client_name, name);
01422       db_set_client_name(hDB, client_name);
01423 
01424       /* set also as rpc name */
01425       rpc_set_name(client_name);
01426 
01427       /* use /system/clients/PID as root */
01428       sprintf(str, "System/Clients/%0d", pid);
01429       db_find_key(hDB, 0, str, &hKey);
01430 
01431       /* set host name */
01432       status = db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
01433       if (status != DB_SUCCESS) {
01434          db_unlock_database(hDB);
01435          return status;
01436       }
01437 
01438       /* set computer id */
01439       status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT);
01440       if (status != DB_SUCCESS) {
01441          db_unlock_database(hDB);
01442          return status;
01443       }
01444 
01445       /* set server port */
01446       data = 0;
01447       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
01448       if (status != DB_SUCCESS) {
01449          db_unlock_database(hDB);
01450          return status;
01451       }
01452 
01453       /* lock client entry */
01454       db_set_mode(hDB, hKey, MODE_READ, TRUE);
01455 
01456       /* get (set) default watchdog timeout */
01457       size = sizeof(watchdog_timeout);
01458       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
01459       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01460 
01461       /* define /programs entry */
01462       sprintf(str, "/Programs/%s", orig_name);
01463       db_create_record(hDB, 0, str, strcomb(program_info_str));
01464 
01465       /* save handle for ODB and client */
01466       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
01467       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
01468 
01469       /* save watchdog timeout */
01470       cm_get_watchdog_params(&call_watchdog, NULL);
01471       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01472       if (call_watchdog)
01473          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
01474 
01475       /* end of atomic operations */
01476       db_unlock_database(hDB);
01477 
01478       /* touch notify key to inform others */
01479       data = 0;
01480       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
01481 
01482       *hKeyClient = hKey;
01483    }
01484 #endif                          /* LOCAL_ROUTINES */
01485 
01486    return CM_SUCCESS;
01487 }
01488 
01489 /********************************************************************/
01490 /**
01491 Get info about the current client
01492 @param  *client_name       Client name.
01493 @return   CM_SUCCESS, CM_UNDEF_EXP
01494 */
01495 INT cm_get_client_info(char *client_name)
01496 {
01497    INT status, length;
01498    HNDLE hDB, hKey;
01499 
01500    /* get root key of client */
01501    cm_get_experiment_database(&hDB, &hKey);
01502    if (!hDB) {
01503       client_name[0] = 0;
01504       return CM_UNDEF_EXP;
01505    }
01506 
01507    status = db_find_key(hDB, hKey, "Name", &hKey);
01508    if (status != DB_SUCCESS)
01509       return status;
01510 
01511    length = NAME_LENGTH;
01512    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
01513    if (status != DB_SUCCESS)
01514       return status;
01515 
01516    return CM_SUCCESS;
01517 }
01518 
01519 /********************************************************************/
01520 /**
01521 Returns MIDAS environment variables.
01522 @attention This function can be used to evaluate the standard MIDAS
01523            environment variables before connecting to an experiment
01524            (see @ref Environment_variables).
01525            The usual way is that the host name and experiment name are first derived
01526            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
01527            They can then be superseded by command line parameters with -h and -e flags.
01528 \code
01529 #include <stdio.h>
01530 #include <midas.h>
01531 main(int argc, char *argv[])
01532 {
01533   INT  status, i;
01534   char host_name[256],exp_name[32];
01535 
01536   // get default values from environment
01537   cm_get_environment(host_name, exp_name);
01538 
01539   // parse command line parameters
01540   for (i=1 ; i<argc ; i++)
01541     {
01542     if (argv[i][0] == '-')
01543       {
01544       if (i+1 >= argc || argv[i+1][0] == '-')
01545         goto usage;
01546       if (argv[i][1] == 'e')
01547         strcpy(exp_name, argv[++i]);
01548       else if (argv[i][1] == 'h')
01549         strcpy(host_name, argv[++i]);
01550       else
01551         {
01552 usage:
01553         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01554         return 1;
01555         }
01556       }
01557     }
01558   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01559   if (status != CM_SUCCESS)
01560     return 1;
01561     ...do anyting...
01562   cm_disconnect_experiment();
01563 }
01564 \endcode
01565 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
01566 @param host_name_size     string length
01567 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
01568 @param exp_name_size      string length
01569 @return CM_SUCCESS
01570 */
01571 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
01572 {
01573    host_name[0] = exp_name[0] = 0;
01574 
01575    if (getenv("MIDAS_SERVER_HOST"))
01576       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
01577 
01578    if (getenv("MIDAS_EXPT_NAME"))
01579       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
01580 
01581    return CM_SUCCESS;
01582 }
01583 
01584 
01585 /**dox***************************************************************/
01586 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01587 
01588 /********************************************************************/
01589 void cm_check_connect(void)
01590 {
01591    if (_hKeyClient)
01592       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
01593 }
01594 
01595 /**dox***************************************************************/
01596 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01597 
01598 /********************************************************************/
01599 /**
01600 This function connects to an existing MIDAS experiment.
01601 This must be the first call in a MIDAS application.
01602 It opens three TCP connection to the remote host (one for RPC calls,
01603 one to send events and one for hot-link notifications from the remote host)
01604 and writes client information into the ODB under /System/Clients.
01605 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
01606 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
01607 experiment name (see @ref Environment_variables).
01608 For that purpose, the function cm_get_environment()
01609 should be called prior to cm_connect_experiment(). If command line
01610 parameters -h and -e are used, the evaluation should be done between
01611 cm_get_environment() and cm_connect_experiment(). The function
01612 cm_disconnect_experiment() must be called before a MIDAS application exits.
01613 \code
01614 #include <stdio.h>
01615 #include <midas.h>
01616 main(int argc, char *argv[])
01617 {
01618   INT  status, i;
01619   char host_name[256],exp_name[32];
01620 
01621   // get default values from environment
01622   cm_get_environment(host_name, exp_name);
01623 
01624   // parse command line parameters
01625   for (i=1 ; i<argc ; i++)
01626     {
01627     if (argv[i][0] == '-')
01628       {
01629       if (i+1 >= argc || argv[i+1][0] == '-')
01630         goto usage;
01631       if (argv[i][1] == 'e')
01632         strcpy(exp_name, argv[++i]);
01633       else if (argv[i][1] == 'h')
01634         strcpy(host_name, argv[++i]);
01635       else
01636         {
01637 usage:
01638         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01639         return 1;
01640         }
01641       }
01642     }
01643   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01644   if (status != CM_SUCCESS)
01645     return 1;
01646   ...do operations...
01647   cm_disconnect_experiment();
01648 }
01649 \endcode
01650 @param host_name Specifies host to connect to. Must be a valid IP host name.
01651   The string can be empty ("") if to connect to the local computer.
01652 @param exp_name Specifies the experiment to connect to.
01653   If this string is empty, the number of defined experiments in exptab is checked.
01654   If only one experiment is defined, the function automatically connects to this
01655   one. If more than one experiment is defined, a list is presented and the user
01656   can interactively select one experiment.
01657 @param client_name Client name of the calling program as it can be seen by
01658   others (like the scl command in ODBEdit).
01659 @param func Callback function to read in a password if security has
01660   been enabled. In all command line applications this function is NULL which
01661   invokes an internal ss_gets() function to read in a password.
01662   In windows environments (MS Windows, X Windows) a function can be supplied to
01663   open a dialog box and read in the password. The argument of this function must
01664   be the returned password.
01665 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br>
01666 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
01667 */
01668 INT cm_connect_experiment(char *host_name, char *exp_name, char *client_name, void (*func) (char *))
01669 {
01670    INT status;
01671    char str[256];
01672 
01673    status = cm_connect_experiment1(host_name, exp_name, client_name,
01674                                    func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
01675    if (status != CM_SUCCESS) {
01676       cm_get_error(status, str);
01677       puts(str);
01678    }
01679 
01680    return status;
01681 }
01682 
01683 /********************************************************************/
01684 /**
01685 Connect to a MIDAS experiment (to the online database) on
01686            a specific host.
01687 @internal
01688 */
01689 INT cm_connect_experiment1(char *host_name, char *exp_name,
01690                            char *client_name, void (*func) (char *), INT odb_size, DWORD watchdog_timeout)
01691 {
01692    INT status, i, mutex_elog, mutex_alarm, size;
01693    char local_host_name[HOST_NAME_LENGTH];
01694    char client_name1[NAME_LENGTH];
01695    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
01696    HNDLE hDB, hKeyClient;
01697    BOOL call_watchdog;
01698    RUNINFO_STR(runinfo_str);
01699 
01700    if (_hKeyClient)
01701       cm_disconnect_experiment();
01702 
01703    rpc_set_name(client_name);
01704 
01705    /* check for local host */
01706    if (equal_ustring(host_name, "local"))
01707       host_name[0] = 0;
01708 
01709 #ifdef OS_WINNT
01710    {
01711       WSADATA WSAData;
01712 
01713       /* Start windows sockets */
01714       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01715          return RPC_NET_ERROR;
01716    }
01717 #endif
01718 
01719    /* search for experiment name in exptab */
01720    if (exp_name == NULL)
01721       exp_name = "";
01722 
01723    strcpy(exp_name1, exp_name);
01724    if (exp_name1[0] == 0) {
01725       status = cm_select_experiment(host_name, exp_name1);
01726       if (status != CM_SUCCESS)
01727          return status;
01728    }
01729 
01730    /* connect to MIDAS server */
01731    if (host_name[0]) {
01732       status = rpc_server_connect(host_name, exp_name1);
01733       if (status != RPC_SUCCESS)
01734          return status;
01735 
01736       /* register MIDAS library functions */
01737       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
01738       if (status != RPC_SUCCESS)
01739          return status;
01740    } else {
01741       /* lookup path for *SHM files and save it */
01742       status = cm_scan_experiments();
01743       if (status != CM_SUCCESS)
01744          return status;
01745 
01746       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
01747          if (equal_ustring(exp_name1, exptab[i].name))
01748             break;
01749 
01750       /* return if experiment not defined */
01751       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
01752          /* message should be displayed by application
01753             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
01754             cm_msg(MERROR, str);
01755           */
01756          return CM_UNDEF_EXP;
01757       }
01758 
01759       cm_set_path(exptab[i].directory);
01760 
01761       /* create alarm and elog mutexes */
01762       status = ss_mutex_create("ALARM", &mutex_alarm);
01763       if (status != SS_CREATED && status != SS_SUCCESS) {
01764          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
01765          return status;
01766       }
01767       status = ss_mutex_create("ELOG", &mutex_elog);
01768       if (status != SS_CREATED && status != SS_SUCCESS) {
01769          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
01770          return status;
01771       }
01772       cm_set_experiment_mutex(mutex_alarm, mutex_elog);
01773    }
01774 
01775    /* open ODB */
01776    if (odb_size == 0)
01777       odb_size = DEFAULT_ODB_SIZE;
01778 
01779    status = db_open_database("ODB", odb_size, &hDB, client_name);
01780    if (status != DB_SUCCESS && status != DB_CREATED) {
01781       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01782       return status;
01783    }
01784 
01785    /* now setup client info */
01786    gethostname(local_host_name, sizeof(local_host_name));
01787 
01788    /* check watchdog timeout */
01789    if (watchdog_timeout == 0)
01790       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01791 
01792    strcpy(client_name1, client_name);
01793    password[0] = 0;
01794    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01795                                client_name1, rpc_get_option(0, RPC_OHW_TYPE), password, watchdog_timeout);
01796 
01797    if (status == CM_WRONG_PASSWORD) {
01798       if (func == NULL)
01799          strcpy(str, ss_getpass("Password: "));
01800       else
01801          func(str);
01802 
01803       /* re-open database */
01804       status = db_open_database("ODB", odb_size, &hDB, client_name);
01805       if (status != DB_SUCCESS && status != DB_CREATED) {
01806          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01807          return status;
01808       }
01809 
01810       strcpy(password, ss_crypt(str, "mi"));
01811       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01812                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE), password, watchdog_timeout);
01813       if (status != CM_SUCCESS) {
01814          /* disconnect */
01815          if (rpc_is_remote())
01816             rpc_server_disconnect();
01817 
01818          return status;
01819       }
01820    }
01821 
01822    cm_set_experiment_database(hDB, hKeyClient);
01823 
01824    /* set experiment name in ODB */
01825    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
01826 
01827    /* set data dir in ODB */
01828    cm_get_path(str);
01829    size = sizeof(str);
01830    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
01831 
01832    /* check /runinfo structure */
01833    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), TRUE);
01834    if (status == DB_STRUCT_MISMATCH) {
01835       cm_msg(MERROR, "cm_connect_experiment1", "Aborting on mismatching /Runinfo structure");
01836       cm_disconnect_experiment();
01837       abort();
01838    }
01839 
01840    /* register server to be able to be called by other clients */
01841    status = cm_register_server();
01842    if (status != CM_SUCCESS)
01843       return status;
01844 
01845    /* set watchdog timeout */
01846    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
01847    size = sizeof(watchdog_timeout);
01848    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
01849    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01850    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01851 
01852    /* send startup notification */
01853    if (strchr(local_host_name, '.'))
01854       *strchr(local_host_name, '.') = 0;
01855 
01856    /* startup message is not displayed */
01857    _message_print = NULL;
01858 
01859    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started", client_name, local_host_name);
01860 
01861    /* enable system and user messages to stdout as default */
01862    cm_set_msg_print(MT_ALL, MT_ALL, puts);
01863 
01864    /* call cm_check_connect when exiting */
01865    atexit((void (*)(void)) cm_check_connect);
01866 
01867    /* register ctrl-c handler */
01868    ss_ctrlc_handler(cm_ctrlc_handler);
01869 
01870    return CM_SUCCESS;
01871 }
01872 
01873 /********************************************************************/
01874 /**
01875 Connect to a MIDAS server and return all defined
01876            experiments in *exp_name[MAX_EXPERIMENTS]
01877 @param  host_name         Internet host name.
01878 @param  exp_name          list of experiment names
01879 @return CM_SUCCESS, RPC_NET_ERROR
01880 */
01881 INT cm_list_experiments(char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
01882 {
01883    INT i, status;
01884    struct sockaddr_in bind_addr;
01885    INT sock;
01886    char str[MAX_EXPERIMENT * NAME_LENGTH];
01887    struct hostent *phe;
01888 
01889    if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
01890       status = cm_scan_experiments();
01891       if (status != CM_SUCCESS)
01892          return status;
01893 
01894       for (i = 0; i < MAX_EXPERIMENT; i++)
01895          strcpy(exp_name[i], exptab[i].name);
01896 
01897       return CM_SUCCESS;
01898    }
01899 #ifdef OS_WINNT
01900    {
01901       WSADATA WSAData;
01902 
01903       /* Start windows sockets */
01904       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01905          return RPC_NET_ERROR;
01906    }
01907 #endif
01908 
01909    /* create a new socket for connecting to remote server */
01910    sock = socket(AF_INET, SOCK_STREAM, 0);
01911    if (sock == -1) {
01912       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
01913       return RPC_NET_ERROR;
01914    }
01915 
01916    /* connect to remote node */
01917    memset(&bind_addr, 0, sizeof(bind_addr));
01918    bind_addr.sin_family = AF_INET;
01919    bind_addr.sin_addr.s_addr = 0;
01920    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
01921 
01922 #ifdef OS_VXWORKS
01923    {
01924       INT host_addr;
01925 
01926       host_addr = hostGetByName(host_name);
01927       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
01928    }
01929 #else
01930    phe = gethostbyname(host_name);
01931    if (phe == NULL) {
01932       cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
01933       return RPC_NET_ERROR;
01934    }
01935    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
01936 #endif
01937 
01938 #ifdef OS_UNIX
01939    do {
01940       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
01941 
01942       /* don't return if an alarm signal was cought */
01943    } while (status == -1 && errno == EINTR);
01944 #else
01945    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
01946 #endif
01947 
01948    if (status != 0) {
01949 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
01950       return RPC_NET_ERROR;
01951    }
01952 
01953    /* request experiment list */
01954    send(sock, "I", 2, 0);
01955 
01956    for (i = 0; i < MAX_EXPERIMENT; i++) {
01957       exp_name[i][0] = 0;
01958       status = recv_string(sock, str, sizeof(str), 1000);
01959 
01960       if (status < 0)
01961          return RPC_NET_ERROR;
01962 
01963       if (status == 0)
01964          break;
01965 
01966       strcpy(exp_name[i], str);
01967    }
01968 
01969    exp_name[i][0] = 0;
01970    closesocket(sock);
01971 
01972    return CM_SUCCESS;
01973 }
01974 
01975 /********************************************************************/
01976 /**
01977 Connect to a MIDAS server and select an experiment
01978            from the experiments available on this server
01979 @internal
01980 @param  host_name         Internet host name.
01981 @param  exp_name          list of experiment names
01982 @return CM_SUCCESS, RPC_NET_ERROR
01983 */
01984 INT cm_select_experiment(char *host_name, char *exp_name)
01985 {
01986    INT status, i;
01987    char expts[MAX_EXPERIMENT][NAME_LENGTH];
01988    char str[32];
01989 
01990    /* retrieve list of experiments and make selection */
01991    status = cm_list_experiments(host_name, expts);
01992    if (status != CM_SUCCESS)
01993       return status;
01994 
01995    if (expts[1][0]) {
01996       if (host_name[0])
01997          printf("Available experiments on server %s:\n", host_name);
01998       else
01999          printf("Available experiments on local computer:\n");
02000 
02001       for (i = 0; expts[i][0]; i++)
02002          printf("%d : %s\n", i, expts[i]);
02003       printf("Select number: ");
02004       ss_gets(str, 32);
02005       i = atoi(str);
02006       strcpy(exp_name, expts[i]);
02007    } else
02008       strcpy(exp_name, expts[0]);
02009 
02010    return CM_SUCCESS;
02011 }
02012 
02013 /********************************************************************/
02014 /**
02015 Connect to a MIDAS client of the current experiment
02016 @internal
02017 @param  client_name       Name of client to connect to. This name
02018                             is set by the other client via the
02019                             cm_connect_experiment call.
02020 @param  hConn            Connection handle
02021 @return CM_SUCCESS, CM_NO_CLIENT
02022 */
02023 INT cm_connect_client(char *client_name, HNDLE * hConn)
02024 {
02025    HNDLE hDB, hKeyRoot, hSubkey, hKey;
02026    INT status, i, length, port;
02027    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
02028 
02029    /* find client entry in ODB */
02030    cm_get_experiment_database(&hDB, &hKey);
02031 
02032    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02033    if (status != DB_SUCCESS)
02034       return status;
02035 
02036    i = 0;
02037    do {
02038       /* search for client with specific name */
02039       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02040       if (status == DB_NO_MORE_SUBKEYS)
02041          return CM_NO_CLIENT;
02042 
02043       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02044       if (status != DB_SUCCESS)
02045          return status;
02046 
02047       length = NAME_LENGTH;
02048       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02049       if (status != DB_SUCCESS)
02050          return status;
02051 
02052       if (equal_ustring(name, client_name)) {
02053          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02054          if (status != DB_SUCCESS)
02055             return status;
02056 
02057          length = sizeof(INT);
02058          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02059          if (status != DB_SUCCESS)
02060             return status;
02061 
02062          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02063          if (status != DB_SUCCESS)
02064             return status;
02065 
02066          length = sizeof(host_name);
02067          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02068          if (status != DB_SUCCESS)
02069             return status;
02070 
02071          /* client found -> connect to its server port */
02072          return rpc_client_connect(host_name, port, client_name, hConn);
02073       }
02074 
02075 
02076    } while (TRUE);
02077 }
02078 
02079 /********************************************************************/
02080 /**
02081 Disconnect from a MIDAS client
02082 @param   hConn             Connection handle obtained via
02083                              cm_connect_client()
02084 @param   bShutdown         If TRUE, disconnect from client and
02085                              shut it down (exit the client program)
02086                              by sending a RPC_SHUTDOWN message
02087 @return   see rpc_client_disconnect()
02088 */
02089 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02090 {
02091    return rpc_client_disconnect(hConn, bShutdown);
02092 }
02093 
02094 /********************************************************************/
02095 /**
02096 Disconnect from a MIDAS experiment.
02097 @attention Should be the last call to a MIDAS library function in an
02098 application before it exits. This function removes the client information
02099 from the ODB, disconnects all TCP connections and frees all internal
02100 allocated memory. See cm_connect_experiment() for example.
02101 @return CM_SUCCESS
02102 */
02103 INT cm_disconnect_experiment(void)
02104 {
02105    HNDLE hDB, hKey;
02106    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02107 
02108    /* send shutdown notification */
02109    rpc_get_name(client_name);
02110    gethostname(local_host_name, sizeof(local_host_name));
02111    if (strchr(local_host_name, '.'))
02112       *strchr(local_host_name, '.') = 0;
02113 
02114    /* disconnect message not displayed */
02115    _message_print = NULL;
02116 
02117    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name, local_host_name);
02118 
02119    if (rpc_is_remote()) {
02120       /* close open records */
02121       db_close_all_records();
02122 
02123       rpc_client_disconnect(-1, FALSE);
02124       rpc_server_disconnect();
02125    } else {
02126       rpc_client_disconnect(-1, FALSE);
02127 
02128 #ifdef LOCAL_ROUTINES
02129       ss_alarm(0, cm_watchdog);
02130       _watchdog_last_called = 0;
02131 #endif                          /* LOCAL_ROUTINES */
02132 
02133       /* delete client info */
02134       cm_get_experiment_database(&hDB, &hKey);
02135 
02136       if (hDB)
02137          cm_delete_client_info(hDB, 0);
02138 
02139       bm_close_all_buffers();
02140       db_close_all_databases();
02141    }
02142 
02143    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02144       rpc_server_shutdown();
02145 
02146    /* free RPC list */
02147    rpc_deregister_functions();
02148 
02149    cm_set_experiment_database(0, 0);
02150 
02151    _msg_buffer = 0;
02152 
02153    /* free memory buffers */
02154    if (_event_buffer_size > 0) {
02155       M_FREE(_event_buffer);
02156       _event_buffer_size = 0;
02157    }
02158 
02159    if (_net_recv_buffer_size > 0) {
02160       M_FREE(_net_recv_buffer);
02161       _net_recv_buffer_size = 0;
02162    }
02163 
02164    if (_net_send_buffer_size > 0) {
02165       M_FREE(_net_send_buffer);
02166       _net_send_buffer_size = 0;
02167    }
02168 
02169    if (_tcp_buffer != NULL) {
02170       M_FREE(_tcp_buffer);
02171       _tcp_buffer = NULL;
02172    }
02173 
02174    return CM_SUCCESS;
02175 }
02176 
02177 /********************************************************************/
02178 /**
02179 Set the handle to the ODB for the currently connected experiment
02180 @param hDB              Database handle
02181 @param hKeyClient       Key handle of client structure
02182 @return CM_SUCCESS
02183 */
02184 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02185 {
02186    _hDB = hDB;
02187    _hKeyClient = hKeyClient;
02188 
02189    return CM_SUCCESS;
02190 }
02191 
02192 
02193 
02194 /**dox***************************************************************/
02195 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02196 
02197 /********************************************************************/
02198 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog)
02199 /********************************************************************\
02200 
02201   Routine: cm_set_experiment_mutex
02202 
02203   Purpose: Set the handle to the experiment wide mutexes
02204 
02205   Input:
02206     INT    mutex_alarm      Alarm mutex
02207     INT    mutex_elog       Elog mutex
02208 
02209   Output:
02210     none
02211 
02212   Function value:
02213     CM_SUCCESS              Successful completion
02214 
02215 \********************************************************************/
02216 {
02217    _mutex_alarm = mutex_alarm;
02218    _mutex_elog = mutex_elog;
02219 
02220    return CM_SUCCESS;
02221 }
02222 
02223 /**dox***************************************************************/
02224 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02225 
02226 /********************************************************************/
02227 /**
02228 Get the handle to the ODB from the currently connected experiment.
02229 
02230 @attention This function returns the handle of the online database (ODB) which
02231 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02232 to access the client information in the ODB. If the client key handle is not needed,
02233 the parameter can be NULL.
02234 \code
02235 HNDLE hDB, hkeyclient;
02236  char  name[32];
02237  int   size;
02238  db_get_experiment_database(&hdb, &hkeyclient);
02239  size = sizeof(name);
02240  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02241  printf("My name is %s\n", name);
02242 \endcode
02243 @param hDB Database handle.
02244 @param hKeyClient Handle for key where search starts, zero for root.
02245 @return CM_SUCCESS
02246 */
02247 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02248 {
02249    if (_hDB) {
02250       if (hDB != NULL)
02251          *hDB = _hDB;
02252       if (hKeyClient != NULL)
02253          *hKeyClient = _hKeyClient;
02254    } else {
02255       if (hDB != NULL)
02256          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02257       if (hKeyClient != NULL)
02258          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02259    }
02260 
02261    return CM_SUCCESS;
02262 }
02263 
02264 /**dox***************************************************************/
02265 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02266 
02267 /********************************************************************/
02268 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog)
02269 /********************************************************************\
02270 
02271   Routine: cm_get_experiment_mutex
02272 
02273   Purpose: Get the handle to the experiment wide mutexes
02274 
02275   Input:
02276     none
02277 
02278   Output:
02279     INT    mutex_alarm      Alarm mutex
02280     INT    mutex_elog       Elog mutex
02281 
02282   Function value:
02283     CM_SUCCESS              Successful completion
02284 
02285 \********************************************************************/
02286 {
02287    if (mutex_alarm)
02288       *mutex_alarm = _mutex_alarm;
02289    if (mutex_elog)
02290       *mutex_elog = _mutex_elog;
02291 
02292    return CM_SUCCESS;
02293 }
02294 
02295 /**dox***************************************************************/
02296 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02297 
02298 static int bm_validate_client_index(const BUFFER * buf)
02299 {
02300    int badindex = 0;
02301    BUFFER_CLIENT *bcl = buf->buffer_header->client;
02302 
02303    if (buf->client_index < 0)
02304       badindex = 1;
02305    else if (buf->client_index > buf->buffer_header->max_client_index)
02306       badindex = 1;
02307    else {
02308       bcl = &(buf->buffer_header->client[buf->client_index]);
02309       if (bcl->name[0] == 0)
02310          badindex = 1;
02311       else if (bcl->pid != ss_getpid())
02312          badindex = 1;
02313    }
02314 
02315    if (badindex) {
02316       static int prevent_recursion = 1;
02317       if (prevent_recursion) {
02318          prevent_recursion = 0;
02319          cm_msg(MERROR, "bm_validate_client_index",
02320                 "Invalid client index %d in buffer \'%s\'. Client name \'%s\', pid %d should be %d",
02321                 buf->client_index, buf->buffer_header->name, bcl->name, bcl->pid, ss_getpid());
02322          cm_msg(MERROR, "bm_validate_client_index",
02323                 "Maybe this client was removed by a timeout. Cannot continue, exiting.");
02324       }
02325       exit(1);
02326    }
02327 
02328    return buf->client_index;
02329 }
02330 
02331 /********************************************************************/
02332 /**
02333 Sets the internal watchdog flags and the own timeout.
02334 If call_watchdog is TRUE, the cm_watchdog routine is called
02335 periodically from the system to show other clients that
02336 this application is "alive". On UNIX systems, the
02337 alarm() timer is used which is then not available for
02338 user purposes.
02339 
02340 The timeout specifies the time, after which the calling
02341 application should be considered "dead" by other clients.
02342 Normally, the cm_watchdog() routines is called periodically.
02343 If a client crashes, this does not occur any more. Then
02344 other clients can detect this and clear all buffer and
02345 database entries of this application so they are not
02346 blocked any more. If this application should not checked
02347 by others, the timeout can be specified as zero.
02348 It might be useful for debugging purposes to do so,
02349 because if a debugger comes to a breakpoint and stops
02350 the application, the periodic call of cm_watchdog
02351 is disabled and the client looks like dead.
02352 
02353 If the timeout is not zero, but the watchdog is not
02354 called (call_watchdog == FALSE), the user must ensure
02355 to call cm_watchdog periodically with a period of
02356 WATCHDOG_INTERVAL milliseconds or less.
02357 
02358 An application which calles system routines which block
02359 the alarm signal for some time, might increase the
02360 timeout to the maximum expected blocking time before
02361 issuing the calls. One example is the logger doing
02362 Exabyte tape IO, which can take up to one minute.
02363 @param    call_watchdog   Call the cm_watchdog routine periodically
02364 @param    timeout         Timeout for this application in ms
02365 @return   CM_SUCCESS
02366 */
02367 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
02368 {
02369    INT i;
02370 
02371    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
02372    _watchdog_timeout = timeout;
02373 
02374    if (rpc_is_remote())
02375       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
02376 
02377 #ifdef LOCAL_ROUTINES
02378 
02379    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
02380       HNDLE hDB, hKey;
02381 
02382       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
02383 
02384       /* write timeout value to client enty in ODB */
02385       cm_get_experiment_database(&hDB, &hKey);
02386 
02387       if (hDB) {
02388          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02389          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
02390          db_set_mode(hDB, hKey, MODE_READ, TRUE);
02391       }
02392    } else {
02393       _call_watchdog = call_watchdog;
02394       _watchdog_timeout = timeout;
02395 
02396       /* set watchdog flag of all open buffers */
02397       for (i = _buffer_entries; i > 0; i--) {
02398          BUFFER_CLIENT *pclient;
02399          BUFFER_HEADER *pheader;
02400          INT index;
02401 
02402          index = bm_validate_client_index(&_buffer[i - 1]);
02403          pheader = _buffer[i - 1].buffer_header;
02404          pclient = &pheader->client[index];
02405 
02406          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02407              _buffer[i - 1].index != rpc_get_server_acception())
02408             continue;
02409 
02410          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i - 1].index != ss_gettid())
02411             continue;
02412 
02413          if (!_buffer[i - 1].attached)
02414             continue;
02415 
02416          /* clear entry from client structure in buffer header */
02417          pclient->watchdog_timeout = timeout;
02418 
02419          /* show activity */
02420          pclient->last_activity = ss_millitime();
02421       }
02422 
02423       /* set watchdog flag of alll open databases */
02424       for (i = _database_entries; i > 0; i--) {
02425          DATABASE_HEADER *pheader;
02426          DATABASE_CLIENT *pclient;
02427          INT index;
02428 
02429          db_lock_database(i);
02430          index = _database[i - 1].client_index;
02431          pheader = _database[i - 1].database_header;
02432          pclient = &pheader->client[index];
02433 
02434          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02435              _database[i - 1].index != rpc_get_server_acception()) {
02436             db_unlock_database(i);
02437             continue;
02438          }
02439 
02440          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _database[i - 1].index != ss_gettid()) {
02441             db_unlock_database(i);
02442             continue;
02443          }
02444 
02445          if (!_database[i - 1].attached) {
02446             db_unlock_database(i);
02447             continue;
02448          }
02449 
02450          /* clear entry from client structure in buffer header */
02451          pclient->watchdog_timeout = timeout;
02452 
02453          /* show activity */
02454          pclient->last_activity = ss_millitime();
02455 
02456          db_unlock_database(i);
02457       }
02458 
02459       if (call_watchdog)
02460          /* restart watchdog */
02461          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02462       else
02463          /* kill current timer */
02464          ss_alarm(0, cm_watchdog);
02465    }
02466 
02467 #endif                          /* LOCAL_ROUTINES */
02468 
02469    return CM_SUCCESS;
02470 }
02471 
02472 /********************************************************************/
02473 /**
02474 Return the current watchdog parameters
02475 @param call_watchdog   Call the cm_watchdog routine periodically
02476 @param timeout         Timeout for this application in seconds
02477 @return   CM_SUCCESS
02478 */
02479 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
02480 {
02481    if (call_watchdog)
02482       *call_watchdog = _call_watchdog;
02483    if (timeout)
02484       *timeout = _watchdog_timeout;
02485 
02486    return CM_SUCCESS;
02487 }
02488 
02489 /********************************************************************/
02490 /**
02491 Return watchdog information about specific client
02492 @param    hDB              ODB handle
02493 @param    client_name     ODB client name
02494 @param    timeout         Timeout for this application in seconds
02495 @param    last            Last time watchdog was called in msec
02496 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE
02497 */
02498 
02499 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
02500 {
02501    if (rpc_is_remote())
02502       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
02503 
02504 #ifdef LOCAL_ROUTINES
02505    {
02506       DATABASE_HEADER *pheader;
02507       DATABASE_CLIENT *pclient;
02508       INT i;
02509 
02510       if (hDB > _database_entries || hDB <= 0) {
02511          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02512          return DB_INVALID_HANDLE;
02513       }
02514 
02515       if (!_database[hDB - 1].attached) {
02516          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02517          return DB_INVALID_HANDLE;
02518       }
02519 
02520       /* lock database */
02521       db_lock_database(hDB);
02522 
02523       pheader = _database[hDB - 1].database_header;
02524       pclient = pheader->client;
02525 
02526       /* find client */
02527       for (i = 0; i < pheader->max_client_index; i++, pclient++)
02528          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
02529             *timeout = pclient->watchdog_timeout;
02530             *last = ss_millitime() - pclient->last_activity;
02531             db_unlock_database(hDB);
02532             return CM_SUCCESS;
02533          }
02534 
02535       *timeout = *last = 0;
02536 
02537       db_unlock_database(hDB);
02538 
02539       return CM_NO_CLIENT;
02540    }
02541 #else                           /* LOCAL_ROUTINES */
02542    return CM_SUCCESS;
02543 #endif                          /* LOCAL_ROUTINES */
02544 }
02545 
02546 
02547 /**dox***************************************************************/
02548 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02549 
02550 /********************************************************************/
02551 INT cm_register_server(void)
02552 /********************************************************************\
02553 
02554   Routine: cm_register_server
02555 
02556   Purpose: Register a server which can be called from other clients
02557            of a specific experiment.
02558 
02559   Input:
02560     none
02561 
02562   Output:
02563     none
02564 
02565   Function value:
02566     CM_SUCCESS              Successful completion
02567 
02568 \********************************************************************/
02569 {
02570    INT status, port;
02571    HNDLE hDB, hKey;
02572 
02573    if (!_server_registered) {
02574       port = 0;
02575       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
02576       if (status != RPC_SUCCESS)
02577          return status;
02578       _server_registered = TRUE;
02579 
02580       /* register MIDAS library functions */
02581       rpc_register_functions(rpc_get_internal_list(1), NULL);
02582 
02583       /* store port number in ODB */
02584       cm_get_experiment_database(&hDB, &hKey);
02585 
02586       status = db_find_key(hDB, hKey, "Server Port", &hKey);
02587       if (status != DB_SUCCESS)
02588          return status;
02589 
02590       /* unlock database */
02591       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02592 
02593       /* set value */
02594       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
02595       if (status != DB_SUCCESS)
02596          return status;
02597 
02598       /* lock database */
02599       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02600    }
02601 
02602    return CM_SUCCESS;
02603 }
02604 
02605 /**dox***************************************************************/
02606 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02607 
02608 /********************************************************************/
02609 /**
02610 Registers a callback function for run transitions.
02611 This function internally registers the transition callback
02612 function and publishes its request for transition notification by writing
02613 a transition request to /System/Clients/<pid>/Transition XXX.
02614 Other clients making a transition scan the transition requests of all clients
02615 and call their transition callbacks via RPC.
02616 
02617 Clients can register for transitions (Start/Stop/Pause/Resume) in a given
02618 sequence. All sequence numbers given in the registration are sorted on
02619 a transition and the clients are contacted in ascending order. By default,
02620 all programs register with a sequence number of 500. The logger however
02621 uses 200 for start, so that it can open files before the other clients
02622 are contacted, and 800 for stop, so that the files get closed when all
02623 other clients have gone already through the stop trantition.
02624 
02625 The callback function returns CM_SUCCESS if it can perform the transition or
02626 a value larger than one in case of error. An error string can be copied
02627 into the error variable.
02628 @attention The callback function will be called on transitions from inside the
02629     cm_yield() function which therefore must be contained in the main program loop.
02630 \code
02631 INT start(INT run_number, char *error)
02632 {
02633   if (<not ok>)
02634     {
02635     strcpy(error, "Cannot start because ...");
02636     return 2;
02637     }
02638   printf("Starting run %d\n", run_number);
02639   return CM_SUCCESS;
02640 }
02641 main()
02642 {
02643   ...
02644   cm_register_transition(TR_START, start, 500);
02645   do
02646     {
02647     status = cm_yield(1000);
02648     } while (status != RPC_SHUTDOWN &&
02649              status != SS_ABORT);
02650   ...
02651 }
02652 \endcode
02653 @param transition Transition to register for (see @ref state_transition)
02654 @param func Callback function.
02655 @param sequence_number Sequence number for that transition (1..1000)
02656 @return CM_SUCCESS
02657 */
02658 INT cm_register_transition(INT transition, INT(*func) (INT, char *), INT sequence_number)
02659 {
02660    INT status, i;
02661    HNDLE hDB, hKey, hKeyTrans;
02662    KEY key;
02663    char str[256];
02664 
02665    /* check for valid transition */
02666    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
02667       cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
02668       return CM_INVALID_TRANSITION;
02669    }
02670 
02671    cm_get_experiment_database(&hDB, &hKey);
02672 
02673    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
02674 
02675    /* register new transition request */
02676 
02677    /* find empty slot */
02678    for (i = 0; i < MAX_TRANSITIONS; i++)
02679       if (!_trans_table[i].transition)
02680          break;
02681 
02682    if (i == MAX_TRANSITIONS) {
02683       cm_msg(MERROR, "cm_register_transition",
02684              "To many transition registrations. Please increase MAX_TRANSITIONS and recompile");
02685       return CM_TOO_MANY_REQUESTS;
02686    }
02687 
02688    _trans_table[i].transition = transition;
02689    _trans_table[i].func = func;
02690    _trans_table[i].sequence_number = sequence_number;
02691 
02692    for (i = 0; i < 13; i++)
02693       if (trans_name[i].transition == transition)
02694          break;
02695 
02696    sprintf(str, "Transition %s", trans_name[i].name);
02697 
02698    /* unlock database */
02699    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02700 
02701    /* set value */
02702    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02703    if (!hKeyTrans) {
02704       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02705       if (status != DB_SUCCESS)
02706          return status;
02707    } else {
02708       status = db_get_key(hDB, hKeyTrans, &key);
02709       if (status != DB_SUCCESS)
02710          return status;
02711       status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT);
02712       if (status != DB_SUCCESS)
02713          return status;
02714    }
02715 
02716    /* re-lock database */
02717    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02718 
02719    return CM_SUCCESS;
02720 }
02721 
02722 INT cm_deregister_transition(INT transition)
02723 {
02724    INT status, i;
02725    HNDLE hDB, hKey, hKeyTrans;
02726    char str[256];
02727 
02728    /* check for valid transition */
02729    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
02730       cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
02731       return CM_INVALID_TRANSITION;
02732    }
02733 
02734    cm_get_experiment_database(&hDB, &hKey);
02735 
02736    /* remove existing transition request */
02737    for (i = 0; i < MAX_TRANSITIONS; i++)
02738       if (_trans_table[i].transition == transition)
02739          break;
02740 
02741    if (i == MAX_TRANSITIONS) {
02742       cm_msg(MERROR, "cm_register_transition",
02743              "Cannot de-register transition registration, request not found");
02744       return CM_INVALID_TRANSITION;
02745    }
02746 
02747    _trans_table[i].transition = 0;
02748    _trans_table[i].func = NULL;
02749    _trans_table[i].sequence_number = 0;
02750 
02751    for (i = 0; i < 13; i++)
02752       if (trans_name[i].transition == transition)
02753          break;
02754 
02755    sprintf(str, "Transition %s", trans_name[i].name);
02756 
02757    /* unlock database */
02758    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02759 
02760    /* set value */
02761    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02762    if (hKeyTrans) {
02763       status = db_delete_key(hDB, hKeyTrans, FALSE);
02764       if (status != DB_SUCCESS)
02765          return status;
02766    }
02767 
02768    /* re-lock database */
02769    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02770 
02771    return CM_SUCCESS;
02772 }
02773 
02774 /********************************************************************/
02775 /**
02776 Change the transition sequence for the calling program.
02777 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02778 @param sequence_number New sequence number, should be between 1 and 1000
02779 @return     CM_SUCCESS
02780 */
02781 INT cm_set_transition_sequence(INT transition, INT sequence_number)
02782 {
02783    INT status, i;
02784    HNDLE hDB, hKey;
02785    char str[256];
02786 
02787    /* check for valid transition */
02788    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
02789       cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
02790       return CM_INVALID_TRANSITION;
02791    }
02792 
02793    cm_get_experiment_database(&hDB, &hKey);
02794 
02795    /* Find the transition type from the list */
02796    for (i = 0; i < 13; i++)
02797       if (trans_name[i].transition == transition)
02798          break;
02799    sprintf(str, "Transition %s", trans_name[i].name);
02800 
02801    /* Change local sequence number for this transition type */
02802    for (i = 0; i < MAX_TRANSITIONS; i++)
02803       if (_trans_table[i].transition == transition) {
02804          _trans_table[i].sequence_number = sequence_number;
02805          break;
02806       }
02807 
02808    /* unlock database */
02809    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02810 
02811    /* set value */
02812    status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02813    if (status != DB_SUCCESS)
02814       return status;
02815 
02816    /* re-lock database */
02817    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02818 
02819    return CM_SUCCESS;
02820 
02821 }
02822 
02823 /**dox***************************************************************/
02824 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02825 
02826 static INT _requested_transition;
02827 static DWORD _deferred_transition_mask;
02828 
02829 /**dox***************************************************************/
02830 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02831 
02832 /********************************************************************/
02833 /**
02834 Register a deferred transition handler. If a client is
02835 registered as a deferred transition handler, it may defer
02836 a requested transition by returning FALSE until a certain
02837 condition (like a motor reaches its end position) is
02838 reached.
02839 @param transition      One of TR_xxx
02840 @param (*func)         Function which gets called whenever
02841                        a transition is requested. If it returns
02842                        FALSE, the transition is not performed.
02843 @return CM_SUCCESS,    <error> Error from ODB access
02844 */
02845 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
02846 {
02847    INT status, i, size;
02848    char tr_key_name[256];
02849    HNDLE hDB, hKey;
02850 
02851    cm_get_experiment_database(&hDB, &hKey);
02852 
02853    for (i = 0; _deferred_trans_table[i].transition; i++)
02854       if (_deferred_trans_table[i].transition == transition)
02855          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
02856 
02857    /* set new transition mask */
02858    _deferred_transition_mask |= transition;
02859 
02860    for (i = 0; i < 13; i++)
02861       if (trans_name[i].transition == transition)
02862          break;
02863 
02864    sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
02865 
02866    /* unlock database */
02867    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02868 
02869    /* set value */
02870    i = 0;
02871    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT);
02872    if (status != DB_SUCCESS)
02873       return status;
02874 
02875    /* re-lock database */
02876    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02877 
02878    /* hot link requested transition */
02879    size = sizeof(_requested_transition);
02880    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT, TRUE);
02881    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
02882    status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
02883    if (status != DB_SUCCESS) {
02884       cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
02885       return status;
02886    }
02887 
02888    return CM_SUCCESS;
02889 }
02890 
02891 /********************************************************************/
02892 /**
02893 Check for any deferred transition. If a deferred transition
02894 handler has been registered via the
02895 cm_register_deferred_transition function, this routine
02896 should be called regularly. It checks if a transition
02897 request is pending. If so, it calld the registered handler
02898 if the transition should be done and then actually does
02899 the transition.
02900 @return     CM_SUCCESS, <error>  Error from cm_transition()
02901 */
02902 INT cm_check_deferred_transition()
02903 {
02904    INT i, status;
02905    char str[256];
02906    static BOOL first;
02907 
02908    if (_requested_transition == 0)
02909       first = TRUE;
02910 
02911    if (_requested_transition & _deferred_transition_mask) {
02912       for (i = 0; _deferred_trans_table[i].transition; i++)
02913          if (_deferred_trans_table[i].transition == _requested_transition)
02914             break;
02915 
02916       if (_deferred_trans_table[i].transition == _requested_transition) {
02917          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition, first)) {
02918             status = cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str), SYNC, FALSE);
02919             if (status != CM_SUCCESS)
02920                cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
02921 
02922             /* bypass hotlink and set _requested_transition directly to zero */
02923             _requested_transition = 0;
02924 
02925             return status;
02926          }
02927          first = FALSE;
02928       }
02929    }
02930 
02931    return SUCCESS;
02932 }
02933 
02934 
02935 /**dox***************************************************************/
02936 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02937 
02938 /********************************************************************/
02939 
02940 /**dox***************************************************************/
02941 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02942 
02943 typedef struct {
02944    int sequence_number;
02945    char host_name[HOST_NAME_LENGTH];
02946    char client_name[NAME_LENGTH];
02947    int port;
02948 } TR_CLIENT;
02949 
02950 int tr_compare(const void *arg1, const void *arg2)
02951 {
02952    return ((TR_CLIENT *) arg1)->sequence_number - ((TR_CLIENT *) arg2)->sequence_number;
02953 }
02954 
02955 /********************************************************************/
02956 /**
02957 Performs a run transition (Start/Stop/Pause/Resume).
02958 
02959 Synchronous/Asynchronous flag.
02960 If set to ASYNC, the transition is done
02961 asynchronously, meaning that clients are connected and told to execute their
02962 callback routine, but no result is awaited. The return value is
02963 specified by the transition callback function on the remote clients. If all callbacks
02964 can perform the transition, CM_SUCCESS is returned. If one callback cannot
02965 perform the transition, the return value of this callback is returned from
02966 cm_transition().
02967 The async_flag is usually FALSE so that transition callbacks can block a
02968 run transition in case of problems and return an error string. The only exception are
02969 situations where a run transition is performed automatically by a program which
02970 cannot block in a transition. For example the logger can cause a run stop when a
02971 disk is nearly full but it cannot block in the cm_transition() function since it
02972 has its own run stop callback which must flush buffers and close disk files and
02973 tapes.
02974 \code
02975 ...
02976     i = 1;
02977     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
02978 
02979       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
02980       if (status != CM_SUCCESS)
02981       {
02982         // in case of error
02983         printf("Error: %s\n", str);
02984       }
02985     ...
02986 \endcode
02987 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02988 @param run_number New run number. If zero, use current run number plus one.
02989 @param perror returned error string.
02990 @param strsize Size of error string.
02991 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
02992 @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
02993 @return CM_SUCCESS, <error> error code from remote client
02994 */
02995 INT cm_transition(INT transition, INT run_number, char *perror, INT strsize, INT async_flag, INT debug_flag)
02996 {
02997    INT i, j, status, index, size, sequence_number, port, state, old_timeout, n_tr_clients;
02998    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hConn, hKeyTrans;
02999    DWORD seconds;
03000    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH], str[256], error[256], tr_key_name[256];
03001    char *trname = "unknown";
03002    KEY key;
03003    BOOL deferred;
03004    PROGRAM_INFO program_info;
03005    TR_CLIENT *tr_client;
03006 
03007    deferred = (transition & TR_DEFERRED) > 0;
03008    transition &= ~TR_DEFERRED;
03009 
03010    /* check for valid transition */
03011    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
03012       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03013       return CM_INVALID_TRANSITION;
03014    }
03015 
03016    /* get key of local client */
03017    cm_get_experiment_database(&hDB, &hKeylocal);
03018 
03019    if (perror != NULL)
03020       strcpy(perror, "Success");
03021 
03022    /* if no run number is given, get it from DB */
03023    if (run_number == 0) {
03024       size = sizeof(run_number);
03025       status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
03026       assert(status == SUCCESS);
03027    }
03028 
03029    if (run_number <= 0) {
03030       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
03031       abort();
03032    }
03033 
03034    /* Set new run number in ODB */
03035    if (transition == TR_START) {
03036       if (debug_flag == 1)
03037          printf("Setting run number %d in ODB\n", run_number);
03038       if (debug_flag == 2)
03039          cm_msg(MDEBUG, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
03040 
03041       status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT);
03042       assert(status == SUCCESS);
03043       if (status != DB_SUCCESS)
03044          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
03045    }
03046 
03047    if (deferred) {
03048       /* remove transition request */
03049       i = 0;
03050       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
03051    } else {
03052       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03053       if (status != DB_SUCCESS) {
03054          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03055          return status;
03056       }
03057 
03058       /* check if deferred transition already in progress */
03059       size = sizeof(INT);
03060       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
03061       if (i) {
03062          if (perror)
03063             sprintf(perror, "Deferred transition already in progress");
03064 
03065          return CM_TRANSITION_IN_PROGRESS;
03066       }
03067 
03068       for (i = 0; trans_name[i].name[0] != 0; i++)
03069          if (trans_name[i].transition == transition) {
03070             trname = trans_name[i].name;
03071             break;
03072          }
03073 
03074       sprintf(tr_key_name, "Transition %s DEFERRED", trname);
03075 
03076       /* search database for clients with deferred transition request */
03077       for (i = 0, status = 0;; i++) {
03078          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03079          if (status == DB_NO_MORE_SUBKEYS)
03080             break;
03081 
03082          if (status == DB_SUCCESS) {
03083             size = sizeof(sequence_number);
03084             status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT, FALSE);
03085 
03086             /* if registered for deferred transition, set flag in ODB and return */
03087             if (status == DB_SUCCESS) {
03088                size = NAME_LENGTH;
03089                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03090                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT);
03091 
03092                if (debug_flag == 1)
03093                   printf("---- Transition %s deferred by client \"%s\" ----\n", trname, str);
03094                if (debug_flag == 2)
03095                   cm_msg(MDEBUG, "cm_transition",
03096                          "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname, str);
03097 
03098                if (perror)
03099                   sprintf(perror, "Transition %s deferred by client \"%s\"", trname, str);
03100 
03101                return CM_DEFERRED_TRANSITION;
03102             }
03103          }
03104       }
03105    }
03106 
03107    /* execute programs on start */
03108    if (transition == TR_START) {
03109       str[0] = 0;
03110       size = sizeof(str);
03111       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
03112       if (str[0])
03113          ss_system(str);
03114 
03115       db_find_key(hDB, 0, "/Programs", &hRootKey);
03116       if (hRootKey) {
03117          for (i = 0;; i++) {
03118             status = db_enum_key(hDB, hRootKey, i, &hKey);
03119             if (status == DB_NO_MORE_SUBKEYS)
03120                break;
03121 
03122             db_get_key(hDB, hKey, &key);
03123 
03124             /* don't check "execute on xxx" */
03125             if (key.type != TID_KEY)
03126                continue;
03127 
03128             size = sizeof(program_info);
03129             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03130             if (status != DB_SUCCESS) {
03131                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03132                continue;
03133             }
03134 
03135             if (program_info.auto_start && program_info.start_command[0])
03136                ss_system(program_info.start_command);
03137          }
03138       }
03139    }
03140 
03141    /* set new start time in database */
03142    if (transition == TR_START) {
03143       /* ASCII format */
03144       cm_asctime(str, sizeof(str));
03145       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03146 
03147       /* reset stop time */
03148       seconds = 0;
03149       db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03150 
03151       /* Seconds since 1.1.1970 */
03152       cm_time(&seconds);
03153       db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03154    }
03155 
03156    /* set stop time in database */
03157    if (transition == TR_STOP) {
03158       size = sizeof(state);
03159       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03160       if (status != DB_SUCCESS)
03161          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03162 
03163       if (state != STATE_STOPPED) {
03164          /* stop time binary */
03165          cm_time(&seconds);
03166          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03167          if (status != DB_SUCCESS)
03168             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
03169 
03170          /* stop time ascii */
03171          cm_asctime(str, sizeof(str));
03172          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03173          if (status != DB_SUCCESS)
03174             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
03175       }
03176    }
03177 
03178    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03179    if (status != DB_SUCCESS) {
03180       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03181       return status;
03182    }
03183 
03184    for (i = 0; trans_name[i].name[0] != 0; i++)
03185       if (trans_name[i].transition == transition) {
03186          trname = trans_name[i].name;
03187          break;
03188       }
03189 
03190    if (debug_flag == 1)
03191       printf("---- Transition %s started ----\n", trname);
03192    if (debug_flag == 2)
03193       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s started ----", trname);
03194 
03195    sprintf(tr_key_name, "Transition %s", trname);
03196 
03197    /* search database for clients which registered for transition */
03198    n_tr_clients = 0;
03199    tr_client = NULL;
03200 
03201    for (i = 0, status = 0;; i++) {
03202       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03203       if (status == DB_NO_MORE_SUBKEYS)
03204          break;
03205 
03206       if (status == DB_SUCCESS) {
03207          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
03208 
03209          if (status == DB_SUCCESS) {
03210 
03211             db_get_key(hDB, hKeyTrans, &key);
03212 
03213             for (j = 0; j < key.num_values; j++) {
03214                size = sizeof(sequence_number);
03215                status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT);
03216                assert(status == DB_SUCCESS);
03217 
03218                if (tr_client == NULL)
03219                   tr_client = (TR_CLIENT *) malloc(sizeof(TR_CLIENT));
03220                else
03221                   tr_client = (TR_CLIENT *) realloc(tr_client, sizeof(TR_CLIENT) * (n_tr_clients + 1));
03222                assert(tr_client);
03223 
03224                tr_client[n_tr_clients].sequence_number = sequence_number;
03225 
03226                if (hSubkey == hKeylocal) {
03227                   /* remember own client */
03228                   tr_client[n_tr_clients].port = 0;
03229                } else {
03230                   /* get client info */
03231                   size = sizeof(client_name);
03232                   db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
03233                   strcpy(tr_client[n_tr_clients].client_name, client_name);
03234 
03235                   size = sizeof(port);
03236                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03237                   tr_client[n_tr_clients].port = port;
03238 
03239                   size = sizeof(host_name);
03240                   db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03241                   strcpy(tr_client[n_tr_clients].host_name, host_name);
03242                }
03243 
03244                n_tr_clients++;
03245             }
03246          }
03247       }
03248    }
03249 
03250    /* sort clients according to sequence number */
03251    if (n_tr_clients > 1)
03252       qsort(tr_client, n_tr_clients, sizeof(TR_CLIENT), tr_compare);
03253 
03254    /* contact ordered clients for transition */
03255    for (index = 0; index < n_tr_clients; index++) {
03256       /* erase error string */
03257       error[0] = 0;
03258 
03259       if (debug_flag == 1)
03260          printf("\n==== Found client \"%s\" with sequence number %d\n",
03261                 tr_client[index].client_name, tr_client[index].sequence_number);
03262       if (debug_flag == 2)
03263          cm_msg(MDEBUG, "cm_transition",
03264                 "cm_transition: ==== Found client \"%s\" with sequence number %d",
03265                 tr_client[index].client_name, tr_client[index].sequence_number);
03266 
03267       /* if own client call transition callback directly */
03268       if (tr_client[index].port == 0) {
03269          for (i = 0; _trans_table[i].transition; i++)
03270             if (_trans_table[i].transition == transition)
03271                break;
03272 
03273          /* call registered function */
03274          if (_trans_table[i].transition == transition && _trans_table[i].func) {
03275             if (debug_flag == 1)
03276                printf("Calling local transition callback\n");
03277             if (debug_flag == 2)
03278                cm_msg(MDEBUG, "cm_transition", "cm_transition: Calling local transition callback");
03279 
03280             status = _trans_table[i].func(run_number, error);
03281 
03282             if (debug_flag == 1)
03283                printf("Local transition callback finished\n");
03284             if (debug_flag == 2)
03285                cm_msg(MDEBUG, "cm_transition", "cm_transition: Local transition callback finished");
03286          } else
03287             status = CM_SUCCESS;
03288 
03289          if (perror != NULL)
03290             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ? strlen(error) + 1 : strsize);
03291 
03292          if (status != CM_SUCCESS) {
03293             free(tr_client);
03294             return status;
03295          }
03296 
03297       } else {
03298 
03299          /* contact client if transition mask set */
03300          if (debug_flag == 1)
03301             printf("Connecting to client \"%s\" on host %s...\n",
03302                    tr_client[index].client_name, tr_client[index].host_name);
03303          if (debug_flag == 2)
03304             cm_msg(MDEBUG, "cm_transition",
03305                    "cm_transition: Connecting to client \"%s\" on host %s...",
03306                    tr_client[index].client_name, tr_client[index].host_name);
03307 
03308          /* client found -> connect to its server port */
03309          status = rpc_client_connect(tr_client[index].host_name, tr_client[index].port,
03310                                      tr_client[index].client_name, &hConn);
03311          if (status != RPC_SUCCESS) {
03312             cm_msg(MERROR, "cm_transition",
03313                    "cannot connect to client \"%s\" on host %s, port %d, status %d",
03314                    tr_client[index].client_name, tr_client[index].host_name, tr_client[index].port, status);
03315             return status;
03316          }
03317 
03318          if (debug_flag == 1)
03319             printf("Connection established to client \"%s\" on host %s\n",
03320                    tr_client[index].client_name, tr_client[index].host_name);
03321          if (debug_flag == 2)
03322             cm_msg(MDEBUG, "cm_transition",
03323                    "cm_transition: Connection established to client \"%s\" on host %s",
03324                    tr_client[index].client_name, tr_client[index].host_name);
03325 
03326          /* call RC_TRANSITION on remote client with increased timeout */
03327          old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03328          rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
03329 
03330          /* set FTPC protocol if in async mode */
03331          if (async_flag == ASYNC)
03332             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03333 
03334          if (debug_flag == 1)
03335             printf("Executing RPC transition client \"%s\" on host %s...\n",
03336                    tr_client[index].client_name, tr_client[index].host_name);
03337          if (debug_flag == 2)
03338             cm_msg(MDEBUG, "cm_transition",
03339                    "cm_transition: Executing RPC transition client \"%s\" on host %s...",
03340                    tr_client[index].client_name, tr_client[index].host_name);
03341 
03342          status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
03343                                   run_number, error, strsize, tr_client[index].sequence_number);
03344 
03345          /* reset timeout */
03346          rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03347 
03348          /* reset protocol */
03349          if (async_flag == ASYNC)
03350             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
03351 
03352          if (debug_flag == 1)
03353             printf("RPC transition finished client \"%s\" on host %s with status %d\n",
03354                    tr_client[index].client_name, tr_client[index].host_name, status);
03355          if (debug_flag == 2)
03356             cm_msg(MDEBUG, "cm_transition",
03357                    "cm_transition: RPC transition finished client \"%s\" on host %s with status %d",
03358                    tr_client[index].client_name, tr_client[index].host_name, status);
03359 
03360          if (perror != NULL)
03361             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ? strlen(error) + 1 : strsize);
03362 
03363          if (status != CM_SUCCESS) {
03364             free(tr_client);
03365             return status;
03366          }
03367       }
03368    }
03369 
03370    if (tr_client)
03371       free(tr_client);
03372 
03373    if (debug_flag == 1)
03374       printf("\n---- Transition %s finished ----\n", trname);
03375    if (debug_flag == 2)
03376       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname);
03377 
03378    /* set new run state in database */
03379    if (transition == TR_START || transition == TR_RESUME)
03380       state = STATE_RUNNING;
03381 
03382    if (transition == TR_PAUSE)
03383       state = STATE_PAUSED;
03384 
03385    if (transition == TR_STOP)
03386       state = STATE_STOPPED;
03387 
03388    size = sizeof(state);
03389    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
03390    if (status != DB_SUCCESS)
03391       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
03392 
03393    /* send notification message */
03394    str[0] = 0;
03395    if (transition == TR_START)
03396       sprintf(str, "Run #%d started", run_number);
03397    if (transition == TR_STOP)
03398       sprintf(str, "Run #%d stopped", run_number);
03399    if (transition == TR_PAUSE)
03400       sprintf(str, "Run #%d paused", run_number);
03401    if (transition == TR_RESUME)
03402       sprintf(str, "Run #%d resumed", run_number);
03403 
03404    if (str[0])
03405       cm_msg(MINFO, "cm_transition", str);
03406 
03407    /* lock/unlock ODB values if present */
03408    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
03409    if (hKey && transition == TR_START)
03410       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03411    if (hKey && transition == TR_STOP)
03412       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
03413 
03414    /* flush online database */
03415    if (transition == TR_STOP)
03416       db_flush_database(hDB);
03417 
03418    /* execute/stop programs on stop */
03419    if (transition == TR_STOP) {
03420       str[0] = 0;
03421       size = sizeof(str);
03422       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
03423       if (str[0])
03424          ss_system(str);
03425 
03426       db_find_key(hDB, 0, "/Programs", &hRootKey);
03427       if (hRootKey) {
03428          for (i = 0;; i++) {
03429             status = db_enum_key(hDB, hRootKey, i, &hKey);
03430             if (status == DB_NO_MORE_SUBKEYS)
03431                break;
03432 
03433             db_get_key(hDB, hKey, &key);
03434 
03435             /* don't check "execute on xxx" */
03436             if (key.type != TID_KEY)
03437                continue;
03438 
03439             size = sizeof(program_info);
03440             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03441             if (status != DB_SUCCESS) {
03442                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03443                continue;
03444             }
03445 
03446             if (program_info.auto_stop)
03447                cm_shutdown(key.name, FALSE);
03448          }
03449       }
03450    }
03451 
03452    return CM_SUCCESS;
03453 }
03454 
03455 
03456 
03457 /**dox***************************************************************/
03458 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03459 
03460 /********************************************************************/
03461 INT cm_dispatch_ipc(char *message, int socket)
03462 /********************************************************************\
03463 
03464   Routine: cm_dispatch_ipc
03465 
03466   Purpose: Called from ss_suspend if an IPC message arrives
03467 
03468   Input:
03469     INT   msg               IPC message we got, MSG_ODB/MSG_BM
03470     INT   p1, p2            Optional parameters
03471     int   socket            Optional server socket
03472 
03473   Output:
03474     none
03475 
03476   Function value:
03477     CM_SUCCESS              Successful completion
03478 
03479 \********************************************************************/
03480 {
03481    if (message[0] == 'O') {
03482       HNDLE hDB, hKey;
03483       sscanf(message + 2, "%d %d", &hDB, &hKey);
03484       return db_update_record(hDB, hKey, socket);
03485    }
03486 
03487    /* message == "B  " means "resume event sender" */
03488    if (message[0] == 'B' && message[2] != ' ') {
03489       char str[80];
03490 
03491       strcpy(str, message + 2);
03492       if (strchr(str, ' '))
03493          *strchr(str, ' ') = 0;
03494 
03495       if (socket)
03496          return bm_notify_client(str, socket);
03497       else
03498          return bm_push_event(str);
03499    }
03500 
03501    return CM_SUCCESS;
03502 }
03503 
03504 /********************************************************************/
03505 static BOOL _ctrlc_pressed = FALSE;
03506 
03507 void cm_ctrlc_handler(int sig)
03508 {
03509    if (_ctrlc_pressed) {
03510       printf("Received 2nd break. Hard abort.\n");
03511       exit(0);
03512    }
03513    printf("Received break. Aborting...\n");
03514    _ctrlc_pressed = TRUE;
03515 
03516    ss_ctrlc_handler(cm_ctrlc_handler);
03517 }
03518 
03519 BOOL cm_is_ctrlc_pressed()
03520 {
03521    return _ctrlc_pressed;
03522 }
03523 
03524 void cm_ack_ctrlc_pressed()
03525 {
03526    _ctrlc_pressed = FALSE;
03527 }
03528 
03529 
03530 /**dox***************************************************************/
03531 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03532 
03533 /********************************************************************/
03534 /**
03535 Central yield functions for clients. This routine should
03536 be called in an infinite loop by a client in order to
03537 give the MIDAS system the opportunity to receive commands
03538 over RPC channels, update database records and receive
03539 events.
03540 @param millisec         Timeout in millisec. If no message is
03541                         received during the specified timeout,
03542                         the routine returns. If millisec=-1,
03543                         it only returns when receiving an
03544                         RPC_SHUTDOWN message.
03545 @return CM_SUCCESS, RPC_SHUTDOWN
03546 */
03547 INT cm_yield(INT millisec)
03548 {
03549    INT status;
03550    BOOL bMore;
03551    static DWORD last_checked = 0;
03552 
03553    /* check for ctrl-c */
03554    if (_ctrlc_pressed)
03555       return RPC_SHUTDOWN;
03556 
03557    /* check for available events */
03558    if (rpc_is_remote()) {
03559       bMore = bm_poll_event(TRUE);
03560       if (bMore)
03561          status = ss_suspend(0, 0);
03562       else
03563          status = ss_suspend(millisec, 0);
03564 
03565       return status;
03566    }
03567 
03568    /* check alarms once every 10 seconds */
03569    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
03570       al_check();
03571       last_checked = ss_time();
03572    }
03573 
03574    bMore = bm_check_buffers();
03575 
03576    if (bMore) {
03577       /* if events available, quickly check other IPC channels */
03578       status = ss_suspend(0, 0);
03579    } else {
03580       /* mark event buffers for ready-to-receive */
03581       bm_mark_read_waiting(TRUE);
03582 
03583       status = ss_suspend(millisec, 0);
03584 
03585       /* unmark event buffers for ready-to-receive */
03586       bm_mark_read_waiting(FALSE);
03587    }
03588 
03589    return status;
03590 }
03591 
03592 /********************************************************************/
03593 /**
03594 Executes command via system() call
03595 @param    command          Command string to execute
03596 @param    result           stdout of command
03597 @param    bufsize          string size in byte
03598 @return   CM_SUCCESS
03599 */
03600 INT cm_execute(char *command, char *result, INT bufsize)
03601 {
03602    char str[256];
03603    INT n;
03604    int fh;
03605 
03606    if (rpc_is_remote())
03607       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
03608 
03609    if (bufsize > 0) {
03610       strcpy(str, command);
03611       sprintf(str, "%s > %d.tmp", command, ss_getpid());
03612 
03613       system(str);
03614 
03615       sprintf(str, "%d.tmp", ss_getpid());
03616       fh = open(str, O_RDONLY, 0644);
03617       result[0] = 0;
03618       if (fh) {
03619          n = read(fh, result, bufsize - 1);
03620          result[MAX(0, n)] = 0;
03621          close(fh);
03622       }
03623       remove(str);
03624    } else
03625       system(command);
03626 
03627    return CM_SUCCESS;
03628 }
03629 
03630 
03631 
03632 /**dox***************************************************************/
03633 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03634 
03635 /********************************************************************/
03636 INT cm_register_function(INT id, INT(*func) (INT, void **))
03637 /********************************************************************\
03638 
03639   Routine: cm_register_function
03640 
03641   Purpose: Call rpc_register_function and publish the registered
03642            function under system/clients/<pid>/RPC
03643 
03644   Input:
03645     INT      id             RPC ID
03646     INT      *func          New dispatch function
03647 
03648   Output:
03649    <implicit: func gets copied to rpc_list>
03650 
03651   Function value:
03652    CM_SUCCESS               Successful completion
03653    RPC_INVALID_ID           RPC ID not found
03654 
03655 \********************************************************************/
03656 {
03657    HNDLE hDB, hKey;
03658    INT status;
03659    char str[80];
03660 
03661    status = rpc_register_function(id, func);
03662    if (status != RPC_SUCCESS)
03663       return status;
03664 
03665    cm_get_experiment_database(&hDB, &hKey);
03666 
03667    /* create new key for this id */
03668    status = 1;
03669    sprintf(str, "RPC/%d", id);
03670 
03671    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03672    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
03673    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03674 
03675    if (status != DB_SUCCESS)
03676       return status;
03677 
03678    return CM_SUCCESS;
03679 }
03680 
03681 
03682 /**dox***************************************************************/
03683 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03684 
03685 /**dox***************************************************************/
03686                                                                                                              /** @} *//* end of cmfunctionc */
03687 
03688 /**dox***************************************************************/
03689 /** @addtogroup bmfunctionc
03690  *
03691  *  @{  */
03692 
03693 /********************************************************************\
03694 *                                                                    *
03695 *                 bm_xxx  -  Buffer Manager Functions                *
03696 *                                                                    *
03697 \********************************************************************/
03698 
03699 /********************************************************************/
03700 /**
03701 Check if an event matches a given event request by the
03702 event id and trigger mask
03703 @param event_id      Event ID of request
03704 @param trigger_mask  Trigger mask of request
03705 @param pevent    Pointer to event to check
03706 @return TRUE      if event matches request
03707 */
03708 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
03709 {
03710    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 || (pevent->event_id & 0xF000) == EVENTID_FRAG)
03711       /* fragmented event */
03712       return ((event_id == EVENTID_ALL ||
03713                event_id == (pevent->event_id & 0x0FFF)) &&
03714               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03715 
03716    return ((event_id == EVENTID_ALL ||
03717             event_id == pevent->event_id) &&
03718            (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03719 }
03720 
03721 /********************************************************************/
03722 /**
03723 Open an event buffer.
03724 Two default buffers are created by the system.
03725 The "SYSTEM" buffer is used to
03726 exchange events and the "SYSMSG" buffer is used to exchange system messages.
03727 The name and size of the event buffers is defined in midas.h as
03728 EVENT_BUFFER_NAME and 2*MAX_EVENT_SIZE.
03729 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
03730 enters a main loop. Events are then received in process_event()
03731 \code
03732 #include <stdio.h>
03733 #include "midas.h"
03734 void process_event(HNDLE hbuf, HNDLE request_id,
03735            EVENT_HEADER *pheader, void *pevent)
03736 {
03737   printf("Received event #%d\r",
03738   pheader->serial_number);
03739 }
03740 main()
03741 {
03742   INT status, request_id;
03743   HNDLE hbuf;
03744   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
03745   if (status != CM_SUCCESS)
03746   return 1;
03747   bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
03748   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
03749 
03750   do
03751   {
03752    status = cm_yield(1000);
03753   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
03754   cm_disconnect_experiment();
03755   return 0;
03756 }
03757 \endcode
03758 @param buffer_name Name of buffer
03759 @param buffer_size Default size of buffer in bytes. Can by overwritten with ODB value
03760 @param buffer_handle Buffer handle returned by function
03761 @return BM_SUCCESS, BM_CREATED <br>
03762 BM_NO_SHM Shared memory cannot be created <br>
03763 BM_NO_MUTEX Mutex cannot be created <br>
03764 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
03765 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
03766 different size <br>
03767 BM_INVALID_PARAM Invalid parameter
03768 */
03769 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
03770 {
03771    INT status;
03772 
03773    if (rpc_is_remote()) {
03774       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
03775       bm_mark_read_waiting(TRUE);
03776       return status;
03777    }
03778 #ifdef LOCAL_ROUTINES
03779    {
03780       INT i, handle, size;
03781       BUFFER_CLIENT *pclient;
03782       BOOL shm_created;
03783       HNDLE shm_handle;
03784       BUFFER_HEADER *pheader;
03785       HNDLE hDB, odb_key;
03786       char odb_path[256];
03787 
03788       /* get buffer size from ODB, user parameter as default if not present in ODB */
03789       sprintf(odb_path, "/Experiment/Buffer sizes/%s", buffer_name);
03790       status = cm_get_experiment_database(&hDB, &odb_key);
03791       assert(status == SUCCESS);
03792       size = sizeof(INT);
03793       status = db_get_value(hDB, 0, odb_path, &buffer_size, &size, TID_DWORD, TRUE);
03794 
03795       if (buffer_size <= 0 || buffer_size > 1 * 1024 * 1024 * 1024) {
03796          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size %d", buffer_size);
03797          return BM_INVALID_PARAM;
03798       }
03799 
03800       if (!buffer_name[0]) {
03801          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
03802          return BM_INVALID_PARAM;
03803       }
03804 
03805       /* allocate new space for the new buffer descriptor */
03806       if (_buffer_entries == 0) {
03807          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
03808          memset(_buffer, 0, sizeof(BUFFER));
03809          if (_buffer == NULL) {
03810             *buffer_handle = 0;
03811             return BM_NO_MEMORY;
03812          }
03813 
03814          _buffer_entries = 1;
03815          i = 0;
03816       } else {
03817          /* check if buffer alreay is open */
03818          for (i = 0; i < _buffer_entries; i++)
03819             if (_buffer[i].attached && equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
03820                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03821                    _buffer[i].index != rpc_get_server_acception())
03822                   continue;
03823 
03824                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i].index != ss_gettid())
03825                   continue;
03826 
03827                *buffer_handle = i + 1;
03828                return BM_SUCCESS;
03829             }
03830 
03831          /* check for a deleted entry */
03832          for (i = 0; i < _buffer_entries; i++)
03833             if (!_buffer[i].attached)
03834                break;
03835 
03836          /* if not found, create new one */
03837          if (i == _buffer_entries) {
03838             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
03839             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
03840 
03841             _buffer_entries++;
03842             if (_buffer == NULL) {
03843                _buffer_entries--;
03844                *buffer_handle = 0;
03845                return BM_NO_MEMORY;
03846             }
03847          }
03848       }
03849 
03850       handle = i;
03851 
03852       if (strlen(buffer_name) >= NAME_LENGTH)
03853          buffer_name[NAME_LENGTH] = 0;
03854 
03855       /* reduce buffer size is larger than maximum */
03856 #ifdef MAX_SHM_SIZE
03857       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
03858          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
03859 #endif
03860 
03861       /* open shared memory region */
03862       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
03863                            (void **) &(_buffer[handle].buffer_header), &shm_handle, FALSE);
03864 
03865       if (status != SS_SUCCESS && status != SS_CREATED) {
03866          *buffer_handle = 0;
03867          _buffer_entries--;
03868          return BM_NO_SHM;
03869       }
03870 
03871       pheader = _buffer[handle].buffer_header;
03872 
03873       shm_created = (status == SS_CREATED);
03874 
03875       if (shm_created) {
03876          /* setup header info if buffer was created */
03877          memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
03878 
03879          strcpy(pheader->name, buffer_name);
03880          pheader->size = buffer_size;
03881       } else {
03882          /* check if buffer size is identical */
03883          if (pheader->size != buffer_size) {
03884             cm_msg(MERROR, "bm_open_buffer", "Requested buffer size (%d) differs from existing size (%d)",
03885                    buffer_size, pheader->size);
03886             *buffer_handle = 0;
03887             _buffer_entries--;
03888             return BM_MEMSIZE_MISMATCH;
03889          }
03890       }
03891 
03892       /* create mutex for the buffer */
03893       status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
03894       if (status != SS_CREATED && status != SS_SUCCESS) {
03895          *buffer_handle = 0;
03896          _buffer_entries--;
03897          return BM_NO_MUTEX;
03898       }
03899 
03900       /* first lock buffer */
03901       bm_lock_buffer(handle + 1);
03902 
03903       /*
03904          Now we have a BUFFER_HEADER, so let's setup a CLIENT
03905          structure in that buffer. The information there can also
03906          be seen by other processes.
03907        */
03908 
03909       for (i = 0; i < MAX_CLIENTS; i++)
03910          if (pheader->client[i].pid == 0)
03911             break;
03912 
03913       if (i == MAX_CLIENTS) {
03914          bm_unlock_buffer(handle + 1);
03915          *buffer_handle = 0;
03916          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
03917          return BM_NO_SLOT;
03918       }
03919 
03920       /* store slot index in _buffer structure */
03921       _buffer[handle].client_index = i;
03922 
03923       /*
03924          Save the index of the last client of that buffer so that later only
03925          the clients 0..max_client_index-1 have to be searched through.
03926        */
03927       pheader->num_clients++;
03928       if (i + 1 > pheader->max_client_index)
03929          pheader->max_client_index = i + 1;
03930 
03931       /* setup buffer header and client structure */
03932       pclient = &pheader->client[i];
03933 
03934       memset(pclient, 0, sizeof(BUFFER_CLIENT));
03935       /* use client name previously set by bm_set_name */
03936       cm_get_client_info(pclient->name);
03937       if (pclient->name[0] == 0)
03938          strcpy(pclient->name, "unknown");
03939       pclient->pid = ss_getpid();
03940       pclient->tid = ss_gettid();
03941       pclient->thandle = ss_getthandle();
03942 
03943       ss_suspend_get_port(&pclient->port);
03944 
03945       pclient->read_pointer = pheader->write_pointer;
03946       pclient->last_activity = ss_millitime();
03947 
03948       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
03949 
03950       bm_unlock_buffer(handle + 1);
03951 
03952       /* setup _buffer entry */
03953       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
03954       _buffer[handle].attached = TRUE;
03955       _buffer[handle].shm_handle = shm_handle;
03956       _buffer[handle].callback = FALSE;
03957 
03958       /* remember to which connection acutal buffer belongs */
03959       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
03960          _buffer[handle].index = rpc_get_server_acception();
03961       else
03962          _buffer[handle].index = ss_gettid();
03963 
03964       *buffer_handle = (handle + 1);
03965 
03966       /* initialize buffer counters */
03967       bm_init_buffer_counters(handle + 1);
03968 
03969       /* setup dispatcher for receive events */
03970       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
03971 
03972       if (shm_created)
03973          return BM_CREATED;
03974    }
03975 #endif                          /* LOCAL_ROUTINES */
03976 
03977    return BM_SUCCESS;
03978 }
03979 
03980 /********************************************************************/
03981 /**
03982 Closes an event buffer previously opened with bm_open_buffer().
03983 @param buffer_handle buffer handle
03984 @return BM_SUCCESS, BM_INVALID_HANDLE
03985 */
03986 INT bm_close_buffer(INT buffer_handle)
03987 {
03988    if (rpc_is_remote())
03989       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
03990 
03991 #ifdef LOCAL_ROUTINES
03992    {
03993       BUFFER_CLIENT *pclient;
03994       BUFFER_HEADER *pheader;
03995       INT i, j, index, destroy_flag;
03996 
03997       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
03998          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
03999          return BM_INVALID_HANDLE;
04000       }
04001 
04002       /* check if buffer got already closed */
04003       if (!_buffer[buffer_handle - 1].attached) {
04004          return BM_SUCCESS;
04005       }
04006 
04007       /*
04008          Check if buffer was opened by current thread. This is necessary
04009          in the server process where one thread may not close the buffer
04010          of other threads.
04011        */
04012 
04013       index = bm_validate_client_index(&_buffer[buffer_handle - 1]);
04014       pheader = _buffer[buffer_handle - 1].buffer_header;
04015 
04016       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04017           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04018          return BM_INVALID_HANDLE;
04019 
04020       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04021           _buffer[buffer_handle - 1].index != ss_gettid())
04022          return BM_INVALID_HANDLE;
04023 
04024       if (!_buffer[buffer_handle - 1].attached) {
04025          /* don't produce error, since bm_close_all_buffers() might want to close an
04026             already closed buffer */
04027          return BM_SUCCESS;
04028       }
04029 
04030       /* delete all requests for this buffer */
04031       for (i = 0; i < _request_list_entries; i++)
04032          if (_request_list[i].buffer_handle == buffer_handle)
04033             bm_delete_request(i);
04034 
04035       /* first lock buffer */
04036       bm_lock_buffer(buffer_handle);
04037 
04038       /* mark entry in _buffer as empty */
04039       _buffer[buffer_handle - 1].attached = FALSE;
04040 
04041       /* clear entry from client structure in buffer header */
04042       memset(&(pheader->client[index]), 0, sizeof(BUFFER_CLIENT));
04043 
04044       /* calculate new max_client_index entry */
04045       for (i = MAX_CLIENTS - 1; i >= 0; i--)
04046          if (pheader->client[i].pid != 0)
04047             break;
04048       pheader->max_client_index = i + 1;
04049 
04050       /* count new number of clients */
04051       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04052          if (pheader->client[i].pid != 0)
04053             j++;
04054       pheader->num_clients = j;
04055 
04056       destroy_flag = (pheader->num_clients == 0);
04057 
04058       /* free cache */
04059       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04060          M_FREE(_buffer[buffer_handle - 1].read_cache);
04061       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04062          M_FREE(_buffer[buffer_handle - 1].write_cache);
04063 
04064       /* check if anyone is waiting and wake him up */
04065       pclient = pheader->client;
04066 
04067       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04068          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04069             ss_resume(pclient->port, "B  ");
04070 
04071       /* unmap shared memory, delete it if we are the last */
04072       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04073                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04074 
04075       /* unlock buffer */
04076       bm_unlock_buffer(buffer_handle);
04077 
04078       /* delete mutex */
04079       ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04080 
04081       /* update _buffer_entries */
04082       if (buffer_handle == _buffer_entries)
04083          _buffer_entries--;
04084 
04085       if (_buffer_entries > 0)
04086          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04087       else {
04088          M_FREE(_buffer);
04089          _buffer = NULL;
04090       }
04091    }
04092 #endif                          /* LOCAL_ROUTINES */
04093 
04094    return BM_SUCCESS;
04095 }
04096 
04097 /********************************************************************/
04098 /**
04099 Close all open buffers
04100 @return BM_SUCCESS
04101 */
04102 INT bm_close_all_buffers(void)
04103 {
04104    if (rpc_is_remote())
04105       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04106 
04107 #ifdef LOCAL_ROUTINES
04108    {
04109       INT i;
04110 
04111       for (i = _buffer_entries; i > 0; i--)
04112          bm_close_buffer(i);
04113    }
04114 #endif                          /* LOCAL_ROUTINES */
04115 
04116    return BM_SUCCESS;
04117 }
04118 
04119 /**dox***************************************************************/
04120                                                                                                              /** @} *//* end of bmfunctionc */
04121 
04122 /**dox***************************************************************/
04123 /** @addtogroup cmfunctionc
04124  *
04125  *  @{  */
04126 
04127 /*-- Watchdog routines ---------------------------------------------*/
04128 #ifdef LOCAL_ROUTINES
04129 
04130 /********************************************************************/
04131 /**
04132 Called at periodic intervals, checks if all clients are
04133 alive. If one process died, its client entries are cleaned up.
04134 @param dummy unused!
04135 */
04136 void cm_watchdog(int dummy)
04137 {
04138    BUFFER_HEADER *pheader;
04139    BUFFER_CLIENT *pbclient, *pbctmp;
04140    DATABASE_HEADER *pdbheader;
04141    DATABASE_CLIENT *pdbclient;
04142    KEY *pkey;
04143    DWORD actual_time, interval;
04144    INT client_pid;
04145    INT i, j, k, nc, status;
04146    BOOL bDeleted, time_changed, wrong_interval;
04147    char str[256];
04148 
04149    /* return immediately if watchdog has been disabled in meantime */
04150    if (!_call_watchdog)
04151       return;
04152 
04153    /* tell system services that we are in async mode ... */
04154    ss_set_async_flag(TRUE);
04155 
04156    /* Calculate the time since last watchdog call. Kill clients if they
04157       are inactive for more than the timeout they specified */
04158    actual_time = ss_millitime();
04159    if (_watchdog_last_called == 0)
04160       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04161    interval = actual_time - _watchdog_last_called;
04162 
04163    /* check if system time has been changed more than 10 min */
04164    time_changed = interval < 0 || interval > 600000;
04165    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL || interval > 1.2 * WATCHDOG_INTERVAL;
04166 
04167    if (time_changed)
04168       cm_msg(MINFO, "cm_watchdog",
04169              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04170              _watchdog_last_called, actual_time, interval);
04171 
04172    /* check buffers */
04173    for (i = 0; i < _buffer_entries; i++)
04174       if (_buffer[i].attached) {
04175          /* update the last_activity entry to show that we are alive */
04176          pheader = _buffer[i].buffer_header;
04177          pbclient = pheader->client;
04178          pbclient[bm_validate_client_index(&_buffer[i])].last_activity = actual_time;
04179 
04180          /* don't check other clients if interval is stange */
04181          if (wrong_interval)
04182             continue;
04183 
04184          /* now check other clients */
04185          for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04186             /* If client process has no activity, clear its buffer entry. */
04187             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04188                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04189                bm_lock_buffer(i + 1);
04190                str[0] = 0;
04191 
04192                /* now make again the check with the buffer locked */
04193                actual_time = ss_millitime();
04194                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04195                    actual_time > pbclient->last_activity &&
04196                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04197                   sprintf(str,
04198                           "Client \'%s\' on \'%s\' removed by cm_watchdog (idle %1.1lfs,TO %1.0lfs)",
04199                           pbclient->name, pheader->name,
04200                           (actual_time - pbclient->last_activity) / 1000.0,
04201                           pbclient->watchdog_timeout / 1000.0);
04202 
04203                   /* clear entry from client structure in buffer header */
04204                   memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04205 
04206                   /* calculate new max_client_index entry */
04207                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04208                      if (pheader->client[k].pid != 0)
04209                         break;
04210                   pheader->max_client_index = k + 1;
04211 
04212                   /* count new number of clients */
04213                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04214                      if (pheader->client[k].pid != 0)
04215                         nc++;
04216                   pheader->num_clients = nc;
04217 
04218                   /* check if anyone is waiting and wake him up */
04219                   pbctmp = pheader->client;
04220 
04221                   for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04222                      if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04223                         ss_resume(pbctmp->port, "B  ");
04224 
04225                }
04226 
04227                bm_unlock_buffer(i + 1);
04228 
04229                /* display info message after unlocking buffer */
04230                if (str[0])
04231                   cm_msg(MINFO, "cm_watchdog", str);
04232             }
04233       }
04234 
04235    /* check online databases */
04236    for (i = 0; i < _database_entries; i++)
04237       if (_database[i].attached) {
04238          /* update the last_activity entry to show that we are alive */
04239          pdbheader = _database[i].database_header;
04240          pdbclient = pdbheader->client;
04241          pdbclient[_database[i].client_index].last_activity = actual_time;
04242 
04243          /* don't check other clients if interval is stange */
04244          if (wrong_interval)
04245             continue;
04246 
04247          /* now check other clients */
04248          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04249             /* If client process has no activity, clear its buffer entry. */
04250             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04251                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04252                client_pid = pdbclient->tid;
04253                bDeleted = FALSE;
04254                db_lock_database(i + 1);
04255                str[0] = 0;
04256 
04257                /* now make again the check with the buffer locked */
04258                actual_time = ss_millitime();
04259                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04260                    actual_time > pdbclient->last_activity &&
04261                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04262                   sprintf(str,
04263                           "Client \'%s\' (PID %d) on \'%s\' removed by cm_watchdog (idle %1.1lfs,TO %1.0lfs)",
04264                           pdbclient->name, client_pid, pdbheader->name,
04265                           (actual_time - pdbclient->last_activity) / 1000.0,
04266                           pdbclient->watchdog_timeout / 1000.0);
04267 
04268                   /* decrement notify_count for open records and clear exclusive mode */
04269                   for (k = 0; k < pdbclient->max_index; k++)
04270                      if (pdbclient->open_record[k].handle) {
04271                         pkey = (KEY *) ((char *) pdbheader + pdbclient->open_record[k].handle);
04272                         if (pkey->notify_count > 0)
04273                            pkey->notify_count--;
04274 
04275                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04276                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04277                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04278                      }
04279 
04280                   /* clear entry from client structure in buffer header */
04281                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04282 
04283                   /* calculate new max_client_index entry */
04284                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04285                      if (pdbheader->client[k].pid != 0)
04286                         break;
04287                   pdbheader->max_client_index = k + 1;
04288 
04289                   /* count new number of clients */
04290                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04291                      if (pdbheader->client[k].pid != 0)
04292                         nc++;
04293                   pdbheader->num_clients = nc;
04294                   bDeleted = TRUE;
04295                }
04296 
04297                /* delete client entry before unlocking db */
04298                if (bDeleted) {
04299                   status = cm_delete_client_info(i + 1, client_pid);
04300                   if (status != CM_SUCCESS)
04301                      cm_msg(MERROR, "cm_watchdog", "cannot delete client info");
04302                }
04303 
04304                db_unlock_database(i + 1);
04305 
04306                /* display info message after unlocking db */
04307                if (str[0])
04308                   cm_msg(MINFO, "cm_watchdog", str);
04309             }
04310       }
04311 
04312    _watchdog_last_called = actual_time;
04313 
04314    ss_set_async_flag(FALSE);
04315 
04316    /* Schedule next watchdog call */
04317    if (_call_watchdog)
04318       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04319 }
04320 
04321 /********************************************************************/
04322 /**
04323 Temporarily disable watchdog calling. Used for tape IO
04324 not to interrupt lengthy operations like mount.
04325 @param flag FALSE for disable, TRUE for re-enable
04326 @return CM_SUCCESS
04327 */
04328 INT cm_enable_watchdog(BOOL flag)
04329 {
04330    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
04331    static BOOL call_flag = FALSE;
04332 
04333    if (flag) {
04334       if (call_flag)
04335          cm_set_watchdog_params(TRUE, timeout);
04336    } else {
04337       call_flag = _call_watchdog;
04338       timeout = _watchdog_timeout;
04339       if (call_flag)
04340          cm_set_watchdog_params(FALSE, 0);
04341    }
04342 
04343    return CM_SUCCESS;
04344 }
04345 
04346 #endif                          /* local routines */
04347 
04348 /********************************************************************/
04349 /**
04350 Shutdown (exit) other MIDAS client
04351 @param name           Client name or "all" for all clients
04352 @param bUnique        If true, look for the exact client name.
04353                       If false, look for namexxx where xxx is
04354                       a any number.
04355 
04356 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY
04357 */
04358 INT cm_shutdown(char *name, BOOL bUnique)
04359 {
04360    INT status, return_status, i, size;
04361    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
04362    KEY key;
04363    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
04364    INT port;
04365    DWORD start_time;
04366 
04367    cm_get_experiment_database(&hDB, &hKeyClient);
04368 
04369    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04370    if (status != DB_SUCCESS)
04371       return DB_NO_KEY;
04372 
04373    return_status = CM_NO_CLIENT;
04374 
04375    /* loop over all clients */
04376    for (i = 0;; i++) {
04377       status = db_enum_key(hDB, hKey, i, &hSubkey);
04378       if (status == DB_NO_MORE_SUBKEYS)
04379          break;
04380 
04381       /* don't shutdown ourselves */
04382       if (hSubkey == hKeyClient)
04383          continue;
04384 
04385       if (status == DB_SUCCESS) {
04386          db_get_key(hDB, hSubkey, &key);
04387 
04388          /* contact client */
04389          size = sizeof(client_name);
04390          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04391 
04392          if (!bUnique)
04393             client_name[strlen(name)] = 0;      /* strip number */
04394 
04395          /* check if individual client */
04396          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
04397             continue;
04398 
04399          size = sizeof(port);
04400          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
04401 
04402          size = sizeof(remote_host);
04403          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
04404 
04405          /* client found -> connect to its server port */
04406          status = rpc_client_connect(remote_host, port, client_name, &hConn);
04407          if (status != RPC_SUCCESS) {
04408             return_status = CM_NO_CLIENT;
04409             sprintf(str, "cannot connect to client %s on host %s, port %d", client_name, remote_host, port);
04410             cm_msg(MERROR, "cm_shutdown", str);
04411          } else {
04412             /* call disconnect with shutdown=TRUE */
04413             rpc_client_disconnect(hConn, TRUE);
04414 
04415             /* wait until client has shut down */
04416             start_time = ss_millitime();
04417             do {
04418                ss_sleep(100);
04419                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
04420             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
04421 
04422             if (status == DB_SUCCESS) {
04423                cm_msg(MINFO, "cm_shutdown",
04424                       "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
04425                       client_name);
04426                return_status = CM_NO_CLIENT;
04427             } else {
04428                return_status = CM_SUCCESS;
04429                i--;
04430             }
04431          }
04432       }
04433    }
04434 
04435    return return_status;
04436 }
04437 
04438 /********************************************************************/
04439 /**
04440 Check if a MIDAS client exists in current experiment
04441 @param    name            Client name
04442 @param    bUnique         If true, look for the exact client name.
04443                           If false, look for namexxx where xxx is
04444                           a any number
04445 @return   CM_SUCCESS, CM_NO_CLIENT
04446 */
04447 INT cm_exist(char *name, BOOL bUnique)
04448 {
04449    INT status, i, size;
04450    HNDLE hDB, hKeyClient, hKey, hSubkey;
04451    char client_name[NAME_LENGTH];
04452 
04453    if (rpc_is_remote())
04454       return rpc_call(RPC_CM_EXIST, name, bUnique);
04455 
04456    cm_get_experiment_database(&hDB, &hKeyClient);
04457 
04458    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04459    if (status != DB_SUCCESS)
04460       return DB_NO_KEY;
04461 
04462    /* loop over all clients */
04463    for (i = 0;; i++) {
04464       status = db_enum_key(hDB, hKey, i, &hSubkey);
04465       if (status == DB_NO_MORE_SUBKEYS)
04466          break;
04467 
04468       if (hSubkey == hKeyClient)
04469          continue;
04470 
04471       if (status == DB_SUCCESS) {
04472          /* get client name */
04473          size = sizeof(client_name);
04474          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04475 
04476          if (equal_ustring(client_name, name))
04477             return CM_SUCCESS;
04478 
04479          if (!bUnique) {
04480             client_name[strlen(name)] = 0;      /* strip number */
04481             if (equal_ustring(client_name, name))
04482                return CM_SUCCESS;
04483          }
04484       }
04485    }
04486 
04487    return CM_NO_CLIENT;
04488 }
04489 
04490 /********************************************************************/
04491 /**
04492 Remove hanging clients independent of their watchdog
04493            timeout.
04494 
04495 Since this function does not obey the client watchdog
04496 timeout, it should be only called to remove clients which
04497 have their watchdog checking turned off or which are
04498 known to be dead. The normal client removement is done
04499 via cm_watchdog().
04500 
04501 Currently (Sept. 02) there are two applications for that:
04502 -# The ODBEdit command "cleanup", which can be used to
04503 remove clients which have their watchdog checking off,
04504 like the analyzer started with the "-d" flag for a
04505 debugging session.
04506 -# The frontend init code to remove previous frontends.
04507 This can be helpful if a frontend dies. Normally,
04508 one would have to wait 60 sec. for a crashed frontend
04509 to be removed. Only then one can start again the
04510 frontend. Since the frontend init code contains a
04511 call to cm_cleanup(<frontend_name>), one can restart
04512 a frontend immediately.
04513 
04514 Added ignore_timeout on Nov.03. A logger might have an
04515 increased tiemout of up to 60 sec. because of tape
04516 operations. If ignore_timeout is FALSE, the logger is
04517 then not killed if its inactivity is less than 60 sec.,
04518 while in the previous implementation it was always
04519 killed after 2*WATCHDOG_INTERVAL.
04520 @param    client_name      Client name, if zero check all clients
04521 @param    ignore_timeout   If TRUE, ignore a possible increased
04522                            timeout defined by each client.
04523 @return   CM_SUCCESS
04524 */
04525 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
04526 {
04527    if (rpc_is_remote())
04528       return rpc_call(RPC_CM_CLEANUP, client_name);
04529 
04530 #ifdef LOCAL_ROUTINES
04531    {
04532       BUFFER_HEADER *pheader = NULL;
04533       BUFFER_CLIENT *pbclient, *pbctmp;
04534       DATABASE_HEADER *pdbheader;
04535       DATABASE_CLIENT *pdbclient;
04536       KEY *pkey;
04537       INT client_pid;
04538       INT i, j, k, status, nc;
04539       BOOL bDeleted;
04540       char str[256];
04541       DWORD interval;
04542 
04543       /* check buffers */
04544       for (i = 0; i < _buffer_entries; i++)
04545          if (_buffer[i].attached) {
04546             /* update the last_activity entry to show that we are alive */
04547             pheader = _buffer[i].buffer_header;
04548             pbclient = pheader->client;
04549             pbclient[bm_validate_client_index(&_buffer[i])].last_activity = ss_millitime();
04550 
04551             /* now check other clients */
04552             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04553                if (j != _buffer[i].client_index && pbclient->pid &&
04554                    (client_name == NULL || client_name[0] == 0
04555                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
04556                   if (ignore_timeout)
04557                      interval = 2 * WATCHDOG_INTERVAL;
04558                   else
04559                      interval = pbclient->watchdog_timeout;
04560 
04561                   /* If client process has no activity, clear its buffer entry. */
04562                   if (ss_millitime() - pbclient->last_activity > interval) {
04563                      bm_lock_buffer(i + 1);
04564                      str[0] = 0;
04565 
04566                      /* now make again the check with the buffer locked */
04567                      if (ss_millitime() - pbclient->last_activity > interval) {
04568                         sprintf(str,
04569                                 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs,TO %1.0lfs)",
04570                                 pbclient->name, pheader->name,
04571                                 (ss_millitime() - pbclient->last_activity) / 1000.0, interval / 1000.0);
04572 
04573                         /* clear entry from client structure in buffer header */
04574                         memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04575 
04576                         /* calculate new max_client_index entry */
04577                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04578                            if (pheader->client[k].pid != 0)
04579                               break;
04580                         pheader->max_client_index = k + 1;
04581 
04582                         /* count new number of clients */
04583                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04584                            if (pheader->client[k].pid != 0)
04585                               nc++;
04586                         pheader->num_clients = nc;
04587 
04588                         /* check if anyone is waiting and wake him up */
04589                         pbctmp = pheader->client;
04590 
04591                         for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04592                            if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04593                               ss_resume(pbctmp->port, "B  ");
04594 
04595                      }
04596 
04597                      bm_unlock_buffer(i + 1);
04598 
04599                      /* display info message after unlocking buffer */
04600                      if (str[0])
04601                         cm_msg(MINFO, "cm_cleanup", str);
04602 
04603                      /* go again through whole list */
04604                      j = 0;
04605                   }
04606                }
04607          }
04608 
04609       /* check online databases */
04610       for (i = 0; i < _database_entries; i++)
04611          if (_database[i].attached) {
04612             /* update the last_activity entry to show that we are alive */
04613             db_lock_database(i + 1);
04614 
04615             pdbheader = _database[i].database_header;
04616             pdbclient = pdbheader->client;
04617             pdbclient[_database[i].client_index].last_activity = ss_millitime();
04618 
04619             /* now check other clients */
04620             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04621                if (j != _database[i].client_index && pdbclient->pid &&
04622                    (client_name == NULL || client_name[0] == 0
04623                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
04624                   client_pid = pdbclient->tid;
04625                   if (ignore_timeout)
04626                      interval = 2 * WATCHDOG_INTERVAL;
04627                   else
04628                      interval = pdbclient->watchdog_timeout;
04629 
04630                   /* If client process has no activity, clear its buffer entry. */
04631 
04632                   if (ss_millitime() - pdbclient->last_activity > interval) {
04633                      bDeleted = FALSE;
04634                      str[0] = 0;
04635 
04636                      /* now make again the check with the buffer locked */
04637                      if (ss_millitime() - pdbclient->last_activity > interval) {
04638                         sprintf(str,
04639                                 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs,TO %1.0lfs)",
04640                                 pdbclient->name, pdbheader->name,
04641                                 (ss_millitime() - pdbclient->last_activity) / 1000.0, interval / 1000.0);
04642 
04643                         /* decrement notify_count for open records and clear exclusive mode */
04644                         for (k = 0; k < pdbclient->max_index; k++)
04645                            if (pdbclient->open_record[k].handle) {
04646                               pkey = (KEY *) ((char *) pdbheader + pdbclient->open_record[k].handle);
04647                               if (pkey->notify_count > 0)
04648                                  pkey->notify_count--;
04649 
04650                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04651                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
04652                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04653                            }
04654 
04655                         /* clear entry from client structure in buffer header */
04656                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04657 
04658                         /* calculate new max_client_index entry */
04659                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04660                            if (pdbheader->client[k].pid != 0)
04661                               break;
04662                         pdbheader->max_client_index = k + 1;
04663 
04664                         /* count new number of clients */
04665                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04666                            if (pheader->client[k].pid != 0)
04667                               nc++;
04668                         pdbheader->num_clients = nc;
04669 
04670                         bDeleted = TRUE;
04671                      }
04672 
04673 
04674                      /* delete client entry after unlocking db */
04675                      if (bDeleted) {
04676                         db_unlock_database(i + 1);
04677 
04678                         /* display info message after unlocking buffer */
04679                         cm_msg(MINFO, "cm_cleanup", str);
04680 
04681                         status = cm_delete_client_info(i + 1, client_pid);
04682                         if (status != CM_SUCCESS)
04683                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
04684 
04685                         /* re-lock database */
04686                         db_lock_database(i + 1);
04687                         pdbheader = _database[i].database_header;
04688                         pdbclient = pdbheader->client;
04689 
04690                         /* go again though whole list */
04691                         j = 0;
04692                      }
04693                   }
04694                }
04695 
04696             db_unlock_database(i + 1);
04697          }
04698 
04699    }
04700 #endif                          /* LOCAL_ROUTINES */
04701 
04702    return CM_SUCCESS;
04703 }
04704 
04705 /**dox***************************************************************/
04706 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04707 
04708 /********************************************************************/
04709 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
04710 /********************************************************************\
04711 
04712   Routine: bm_buffer_info
04713 
04714   Purpose: Copies the current buffer header referenced by buffer_handle
04715            into the *buffer_header structure which must be supplied
04716            by the calling routine.
04717 
04718   Input:
04719     INT buffer_handle       Handle of the buffer to get the header from
04720 
04721   Output:
04722     BUFFER_HEADER *buffer_header   Destination address which gets a copy
04723                                    of the buffer header structure.
04724 
04725   Function value:
04726     BM_SUCCESS              Successful completion
04727     BM_INVALID_HANDLE       Buffer handle is invalid
04728     RPC_NET_ERROR           Network error
04729 
04730 \********************************************************************/
04731 {
04732    if (rpc_is_remote())
04733       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
04734 
04735 #ifdef LOCAL_ROUTINES
04736 
04737    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04738       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04739       return BM_INVALID_HANDLE;
04740    }
04741 
04742    if (!_buffer[buffer_handle - 1].attached) {
04743       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04744       return BM_INVALID_HANDLE;
04745    }
04746 
04747    bm_lock_buffer(buffer_handle);
04748 
04749    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
04750 
04751    bm_unlock_buffer(buffer_handle);
04752 
04753 #endif                          /* LOCAL_ROUTINES */
04754 
04755    return BM_SUCCESS;
04756 }
04757 
04758 /********************************************************************/
04759 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
04760 /********************************************************************\
04761 
04762   Routine: bm_get_buffer_level
04763 
04764   Purpose: Return number of bytes in buffer or in cache
04765 
04766   Input:
04767     INT buffer_handle       Handle of the buffer to get the info
04768 
04769   Output:
04770     INT *n_bytes              Number of bytes in buffer
04771 
04772   Function value:
04773     BM_SUCCESS              Successful completion
04774     BM_INVALID_HANDLE       Buffer handle is invalid
04775     RPC_NET_ERROR           Network error
04776 
04777 \********************************************************************/
04778 {
04779    if (rpc_is_remote())
04780       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
04781 
04782 #ifdef LOCAL_ROUTINES
04783    {
04784       BUFFER *pbuf;
04785       BUFFER_HEADER *pheader;
04786       BUFFER_CLIENT *pclient;
04787 
04788       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04789          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04790          return BM_INVALID_HANDLE;
04791       }
04792 
04793       pbuf = &_buffer[buffer_handle - 1];
04794       pheader = pbuf->buffer_header;
04795 
04796       if (!pbuf->attached) {
04797          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04798          return BM_INVALID_HANDLE;
04799       }
04800 
04801       bm_lock_buffer(buffer_handle);
04802 
04803       pclient = &(pheader->client[bm_validate_client_index(pbuf)]);
04804 
04805       *n_bytes = pheader->write_pointer - pclient->read_pointer;
04806       if (*n_bytes < 0)
04807          *n_bytes += pheader->size;
04808 
04809       bm_unlock_buffer(buffer_handle);
04810 
04811       /* add bytes in cache */
04812       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
04813          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
04814    }
04815 #endif                          /* LOCAL_ROUTINES */
04816 
04817    return BM_SUCCESS;
04818 }
04819 
04820 
04821 
04822 #ifdef LOCAL_ROUTINES
04823 
04824 /********************************************************************/
04825 INT bm_lock_buffer(INT buffer_handle)
04826 /********************************************************************\
04827 
04828   Routine: bm_lock_buffer
04829 
04830   Purpose: Lock a buffer for exclusive access via system mutex calls.
04831 
04832   Input:
04833     INT    bufer_handle     Handle to the buffer to lock
04834   Output:
04835     none
04836 
04837   Function value:
04838     BM_SUCCESS              Successful completion
04839     BM_INVALID_HANDLE       Buffer handle is invalid
04840 
04841 \********************************************************************/
04842 {
04843    int status;
04844 
04845    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04846       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
04847       return BM_INVALID_HANDLE;
04848    }
04849 
04850    status = ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 5 * 60 * 1000);
04851 
04852    if (status != SS_SUCCESS) {
04853       cm_msg(MERROR, "bm_lock_buffer",
04854              "Cannot lock buffer handle %d, ss_mutex_wait_for() status %d", buffer_handle, status);
04855       abort();
04856       return BM_INVALID_HANDLE;
04857    }
04858 
04859    return BM_SUCCESS;
04860 }
04861 
04862 /********************************************************************/
04863 INT bm_unlock_buffer(INT buffer_handle)
04864 /********************************************************************\
04865 
04866   Routine: bm_unlock_buffer
04867 
04868   Purpose: Unlock a buffer via system mutex calls.
04869 
04870   Input:
04871     INT    bufer_handle     Handle to the buffer to lock
04872   Output:
04873     none
04874 
04875   Function value:
04876     BM_SUCCESS              Successful completion
04877     BM_INVALID_HANDLE       Buffer handle is invalid
04878 
04879 \********************************************************************/
04880 {
04881    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04882       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
04883       return BM_INVALID_HANDLE;
04884    }
04885 
04886    ss_mutex_release(_buffer[buffer_handle - 1].mutex);
04887    return BM_SUCCESS;
04888 }
04889 
04890 #endif                          /* LOCAL_ROUTINES */
04891 
04892 /********************************************************************/
04893 INT bm_init_buffer_counters(INT buffer_handle)
04894 /********************************************************************\
04895 
04896   Routine: bm_init_event_counters
04897 
04898   Purpose: Initialize counters for a specific buffer. This routine
04899            should be called at the beginning of a run.
04900 
04901   Input:
04902     INT    buffer_handle    Handle to the buffer to be
04903                             initialized.
04904   Output:
04905     none
04906 
04907   Function value:
04908     BM_SUCCESS              Successful completion
04909     BM_INVALID_HANDLE       Buffer handle is invalid
04910 
04911 \********************************************************************/
04912 {
04913    if (rpc_is_remote())
04914       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
04915 
04916 #ifdef LOCAL_ROUTINES
04917 
04918    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04919       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d", buffer_handle);
04920       return BM_INVALID_HANDLE;
04921    }
04922 
04923    if (!_buffer[buffer_handle - 1].attached) {
04924       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d", buffer_handle);
04925       return BM_INVALID_HANDLE;
04926    }
04927 
04928    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
04929    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
04930 
04931 #endif                          /* LOCAL_ROUTINES */
04932 
04933    return BM_SUCCESS;
04934 }
04935 
04936 /**dox***************************************************************/
04937 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04938 
04939 /**dox***************************************************************/
04940                                                                                                              /** @} *//* end of cmfunctionc */
04941 
04942 /**dox***************************************************************/
04943 /** @addtogroup bmfunctionc
04944  *
04945  *  @{  */
04946 
04947 /********************************************************************/
04948 /**
04949 Modifies buffer cache size.
04950 Without a buffer cache, events are copied to/from the shared
04951 memory event by event.
04952 
04953 To protect processed from accessing the shared memory simultaneously,
04954 semaphores are used. Since semaphore operations are CPU consuming (typically
04955 50-100us) this can slow down the data transfer especially for small events.
04956 By using a cache the number of semaphore operations is reduced dramatically.
04957 Instead writing directly to the shared memory, the events are copied to a
04958 local cache buffer. When this buffer is full, it is copied to the shared
04959 memory in one operation. The same technique can be used when receiving events.
04960 
04961 The drawback of this method is that the events have to be copied twice, once to the
04962 cache and once from the cache to the shared memory. Therefore it can happen that the
04963 usage of a cache even slows down data throughput on a given environment (computer
04964 type, OS type, event size).
04965 The cache size has therefore be optimized manually to maximize data throughput.
04966 @param buffer_handle buffer handle obtained via bm_open_buffer()
04967 @param read_size cache size for reading events in bytes, zero for no cache
04968 @param write_size cache size for writing events in bytes, zero for no cache
04969 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
04970 */
04971 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
04972 /*------------------------------------------------------------------*/
04973 {
04974    if (rpc_is_remote())
04975       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
04976 
04977 #ifdef LOCAL_ROUTINES
04978    {
04979       BUFFER *pbuf;
04980 
04981       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04982          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
04983          return BM_INVALID_HANDLE;
04984       }
04985 
04986       if (!_buffer[buffer_handle - 1].attached) {
04987          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
04988          return BM_INVALID_HANDLE;
04989       }
04990 
04991       if (read_size < 0 || read_size > 1E6) {
04992          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
04993          return BM_INVALID_PARAM;
04994       }
04995 
04996       if (write_size < 0 || write_size > 1E6) {
04997          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
04998          return BM_INVALID_PARAM;
04999       }
05000 
05001       /* manage read cache */
05002       pbuf = &_buffer[buffer_handle - 1];
05003 
05004       if (pbuf->read_cache_size > 0)
05005          M_FREE(pbuf->read_cache);
05006 
05007       if (read_size > 0) {
05008          pbuf->read_cache = (char *) M_MALLOC(read_size);
05009          if (pbuf->read_cache == NULL) {
05010             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate cache buffer");
05011             return BM_NO_MEMORY;
05012          }
05013       }
05014 
05015       pbuf->read_cache_size = read_size;
05016       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05017 
05018       /* manage write cache */
05019       if (pbuf->write_cache_size > 0)
05020          M_FREE(pbuf->write_cache);
05021 
05022       if (write_size > 0) {
05023          pbuf->write_cache = (char *) M_MALLOC(write_size);
05024          if (pbuf->write_cache == NULL) {
05025             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate cache buffer");
05026             return BM_NO_MEMORY;
05027          }
05028       }
05029 
05030       pbuf->write_cache_size = write_size;
05031       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05032 
05033    }
05034 #endif                          /* LOCAL_ROUTINES */
05035 
05036    return BM_SUCCESS;
05037 }
05038 
05039 /********************************************************************/
05040 /**
05041 Compose a Midas event header.
05042 An event header can usually be set-up manually or
05043 through this routine. If the data size of the event is not known when
05044 the header is composed, it can be set later with event_header->data-size = <...>
05045 Following structure is created at the beginning of an event
05046 \code
05047 typedef struct {
05048  short int     event_id;
05049  short int     trigger_mask;
05050  DWORD         serial_number;
05051  DWORD         time_stamp;
05052  DWORD         data_size;
05053 } EVENT_HEADER;
05054 
05055 char event[1000];
05056  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05057  *(event+sizeof(EVENT_HEADER)) = <...>
05058 \endcode
05059 @param event_header pointer to the event header
05060 @param event_id event ID of the event
05061 @param trigger_mask trigger mask of the event
05062 @param size size if the data part of the event in bytes
05063 @param serial serial number
05064 @return BM_SUCCESS
05065 */
05066 INT bm_compose_event(EVENT_HEADER * event_header,
05067                      short int event_id, short int trigger_mask, DWORD size, DWORD serial)
05068 {
05069    event_header->event_id = event_id;
05070    event_header->trigger_mask = trigger_mask;
05071    event_header->data_size = size;
05072    event_header->time_stamp = ss_time();
05073    event_header->serial_number = serial;
05074 
05075    return BM_SUCCESS;
05076 }
05077 
05078 
05079 /**dox***************************************************************/
05080 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05081 
05082 /********************************************************************/
05083 INT bm_add_event_request(INT buffer_handle, short int event_id,
05084                          short int trigger_mask,
05085                          INT sampling_type,
05086                          void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *), INT request_id)
05087 /********************************************************************\
05088 
05089   Routine:  bm_add_event_request
05090 
05091   Purpose:  Place a request for a specific event type in the client
05092             structure of the buffer refereced by buffer_handle.
05093 
05094   Input:
05095     INT          buffer_handle  Handle to the buffer where the re-
05096                                 quest should be placed in
05097 
05098     short int    event_id       Event ID      \
05099     short int    trigger_mask   Trigger mask  / Event specification
05100 
05101     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05102 
05103 
05104                  Note: to request all types of events, use
05105                    event_id = 0 (all others should be !=0 !)
05106                    trigger_mask = TRIGGER_ALL
05107                    sampling_typ = GET_ALL
05108 
05109 
05110     void         *func          Callback function
05111     INT          request_id     Request id (unique number assigned
05112                                 by bm_request_event)
05113 
05114   Output:
05115     none
05116 
05117   Function value:
05118     BM_SUCCESS              Successful completion
05119     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05120                             MIDAS.H should be increased.
05121     BM_INVALID_HANDLE       Buffer handle is invalid
05122     RPC_NET_ERROR           Network error
05123 
05124 \********************************************************************/
05125 {
05126    if (rpc_is_remote())
05127       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05128                       trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
05129 
05130 #ifdef LOCAL_ROUTINES
05131    {
05132       INT i;
05133       BUFFER_CLIENT *pclient;
05134 
05135       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05136          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d", buffer_handle);
05137          return BM_INVALID_HANDLE;
05138       }
05139 
05140       if (!_buffer[buffer_handle - 1].attached) {
05141          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d", buffer_handle);
05142          return BM_INVALID_HANDLE;
05143       }
05144 
05145       /* avoid callback/non callback requests */
05146       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05147          cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
05148          return BM_INVALID_MIXING;
05149       }
05150 
05151       /* get a pointer to the proper client structure */
05152       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05153                   client[bm_validate_client_index(&_buffer[buffer_handle - 1])]);
05154 
05155       /* lock buffer */
05156       bm_lock_buffer(buffer_handle);
05157 
05158       /* look for a empty request entry */
05159       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05160          if (!pclient->event_request[i].valid)
05161             break;
05162 
05163       if (i == MAX_EVENT_REQUESTS) {
05164          bm_unlock_buffer(buffer_handle);
05165          return BM_NO_MEMORY;
05166       }
05167 
05168       /* setup event_request structure */
05169       pclient->event_request[i].id = request_id;
05170       pclient->event_request[i].valid = TRUE;
05171       pclient->event_request[i].event_id = event_id;
05172       pclient->event_request[i].trigger_mask = trigger_mask;
05173       pclient->event_request[i].sampling_type = sampling_type;
05174       pclient->event_request[i].dispatch = func;
05175 
05176       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05177 
05178       /* set callback flag in buffer structure */
05179       if (func != NULL)
05180          _buffer[buffer_handle - 1].callback = TRUE;
05181 
05182       /*
05183          Save the index of the last request in the list so that later only the
05184          requests 0..max_request_index-1 have to be searched through.
05185        */
05186 
05187       if (i + 1 > pclient->max_request_index)
05188          pclient->max_request_index = i + 1;
05189 
05190       bm_unlock_buffer(buffer_handle);
05191    }
05192 #endif                          /* LOCAL_ROUTINES */
05193 
05194    return BM_SUCCESS;
05195 }
05196 
05197 /**dox***************************************************************/
05198 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05199 
05200 /********************************************************************/
05201 /**
05202 Place an event request based on certain characteristics.
05203 Multiple event requests can be placed for each buffer, which
05204 are later identified by their request ID. They can contain different callback
05205 routines. Example see bm_open_buffer() and bm_receive_event()
05206 @param buffer_handle buffer handle obtained via bm_open_buffer()
05207 @param event_id event ID for requested events. Use EVENTID_ALL
05208 to receive events with any ID.
05209 @param trigger_mask trigger mask for requested events.
05210 The requested events must have at least one bit in its
05211 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05212 receive events with any trigger mask.
05213 @param sampling_type specifies how many events to receive.
05214 A value of GET_ALL receives all events which
05215 match the specified event ID and trigger mask. If the events are consumed slower
05216 than produced, the producer is automatically slowed down. A value of GET_SOME
05217 receives as much events as possible without slowing down the producer. GET_ALL is
05218 typically used by the logger, while GET_SOME is typically used by analyzers.
05219 @param request_id request ID returned by the function.
05220 This ID is passed to the callback routine and must
05221 be used in the bm_delete_request() routine.
05222 @param func allback routine which gets called when an event of the
05223 specified type is received.
05224 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05225 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05226 should be increased.
05227 */
05228 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05229                      short int trigger_mask,
05230                      INT sampling_type, HNDLE * request_id,
05231                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05232 {
05233    INT index, status;
05234 
05235    /* allocate new space for the local request list */
05236    if (_request_list_entries == 0) {
05237       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05238       memset(_request_list, 0, sizeof(REQUEST_LIST));
05239       if (_request_list == NULL) {
05240          cm_msg(MERROR, "bm_request_event", "not enough memory to allocate request list buffer");
05241          return BM_NO_MEMORY;
05242       }
05243 
05244       _request_list_entries = 1;
05245       index = 0;
05246    } else {
05247       /* check for a deleted entry */
05248       for (index = 0; index < _request_list_entries; index++)
05249          if (!_request_list[index].buffer_handle)
05250             break;
05251 
05252       /* if not found, create new one */
05253       if (index == _request_list_entries) {
05254          _request_list = (REQUEST_LIST *) realloc(_request_list,
05255                                                   sizeof(REQUEST_LIST) * (_request_list_entries + 1));
05256          if (_request_list == NULL) {
05257             cm_msg(MERROR, "bm_request_event", "not enough memory to allocate request list buffer");
05258             return BM_NO_MEMORY;
05259          }
05260 
05261          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05262 
05263          _request_list_entries++;
05264       }
05265    }
05266 
05267    /* initialize request list */
05268    _request_list[index].buffer_handle = buffer_handle;
05269    _request_list[index].event_id = event_id;
05270    _request_list[index].trigger_mask = trigger_mask;
05271    _request_list[index].dispatcher = func;
05272 
05273    *request_id = index;
05274 
05275    /* add request in buffer structure */
05276    status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, index);
05277    if (status != BM_SUCCESS)
05278       return status;
05279 
05280    return BM_SUCCESS;
05281 }
05282 
05283 /********************************************************************/
05284 /**
05285 Delete a previously placed request for a specific event
05286 type in the client structure of the buffer refereced by buffer_handle.
05287 @param buffer_handle  Handle to the buffer where the re-
05288                                 quest should be placed in
05289 @param request_id     Request id returned by bm_request_event
05290 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR
05291 */
05292 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05293 {
05294    if (rpc_is_remote())
05295       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
05296 
05297 #ifdef LOCAL_ROUTINES
05298    {
05299       INT i, deleted;
05300       BUFFER_CLIENT *pclient;
05301 
05302       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05303          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d", buffer_handle);
05304          return BM_INVALID_HANDLE;
05305       }
05306 
05307       if (!_buffer[buffer_handle - 1].attached) {
05308          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d", buffer_handle);
05309          return BM_INVALID_HANDLE;
05310       }
05311 
05312       /* get a pointer to the proper client structure */
05313       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05314                   client[bm_validate_client_index(&_buffer[buffer_handle - 1])]);
05315 
05316       /* lock buffer */
05317       bm_lock_buffer(buffer_handle);
05318 
05319       /* check all requests and set to zero if matching */
05320       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
05321          if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
05322             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
05323             deleted++;
05324          }
05325 
05326       /* calculate new max_request_index entry */
05327       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
05328          if (pclient->event_request[i].valid)
05329             break;
05330 
05331       pclient->max_request_index = i + 1;
05332 
05333       /* caluclate new all_flag */
05334       pclient->all_flag = FALSE;
05335 
05336       for (i = 0; i < pclient->max_request_index; i++)
05337          if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
05338             pclient->all_flag = TRUE;
05339             break;
05340          }
05341 
05342       bm_unlock_buffer(buffer_handle);
05343 
05344       if (!deleted)
05345          return BM_NOT_FOUND;
05346    }
05347 #endif                          /* LOCAL_ROUTINES */
05348 
05349    return BM_SUCCESS;
05350 }
05351 
05352 /********************************************************************/
05353 /**
05354 Deletes an event request previously done with bm_request_event().
05355 When an event request gets deleted, events of that requested type are
05356 not received any more. When a buffer is closed via bm_close_buffer(), all
05357 event requests from that buffer are deleted automatically
05358 @param request_id request identifier given by bm_request_event()
05359 @return BM_SUCCESS, BM_INVALID_HANDLE
05360 */
05361 INT bm_delete_request(INT request_id)
05362 {
05363    if (request_id < 0 || request_id >= _request_list_entries)
05364       return BM_INVALID_HANDLE;
05365 
05366    /* remove request entry from buffer */
05367    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
05368 
05369    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
05370 
05371    return BM_SUCCESS;
05372 }
05373 
05374 #if 0 // currently not used
05375 static void bm_show_pointers(const BUFFER_HEADER * pheader)
05376 {
05377    int i;
05378    const BUFFER_CLIENT *pclient;
05379 
05380    pclient = pheader->client;
05381 
05382    printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
05383           pheader->write_pointer, pheader->size);
05384    for (i = 0; i < pheader->max_client_index; i++)
05385       if (pclient[i].pid) {
05386          printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
05387       }
05388 
05389    printf("done\n");
05390 }
05391 #endif
05392 
05393 static void bm_validate_client_pointers(BUFFER_HEADER * pheader, BUFFER_CLIENT * pclient)
05394 {
05395    if (pheader->read_pointer <= pheader->write_pointer) {
05396 
05397       if (pclient->read_pointer < pheader->read_pointer) {
05398          cm_msg(MINFO, "bm_validate_client_pointers",
05399                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05400                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05401 
05402          pclient->read_pointer = pheader->read_pointer;
05403       }
05404 
05405       if (pclient->read_pointer > pheader->write_pointer) {
05406          cm_msg(MINFO, "bm_validate_client_pointers",
05407                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05408                 pheader->name, pclient->read_pointer, pheader->write_pointer);
05409 
05410          pclient->read_pointer = pheader->write_pointer;
05411       }
05412 
05413    } else {
05414 
05415       if (pclient->read_pointer < 0) {
05416          cm_msg(MINFO, "bm_validate_client_pointers",
05417                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05418                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05419 
05420          pclient->read_pointer = pheader->read_pointer;
05421       }
05422 
05423       if (pclient->read_pointer >= pheader->size) {
05424          cm_msg(MINFO, "bm_validate_client_pointers",
05425                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05426                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05427 
05428          pclient->read_pointer = pheader->read_pointer;
05429       }
05430 
05431       if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
05432          cm_msg(MINFO, "bm_validate_client_pointers",
05433                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05434                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05435 
05436          pclient->read_pointer = pheader->read_pointer;
05437       }
05438    }
05439 }
05440 
05441 #if 0 // currently not used
05442 static void bm_validate_pointers(BUFFER_HEADER * pheader)
05443 {
05444    BUFFER_CLIENT *pclient = pheader->client;
05445    int i;
05446 
05447    for (i = 0; i < pheader->max_client_index; i++)
05448       if (pclient[i].pid) {
05449          bm_validate_client_pointers(pheader, &pclient[i]);
05450       }
05451 }
05452 #endif
05453 
05454 static BOOL bm_update_read_pointer(const char *caller_name, BUFFER_HEADER * pheader)
05455 {
05456    BOOL did_move;
05457    int i;
05458    int min_rp;
05459    BUFFER_CLIENT *pclient;
05460 
05461    pclient = pheader->client;
05462 
05463    /* calculate global read pointer as "minimum" of client read pointers */
05464    min_rp = pheader->write_pointer;
05465 
05466    for (i = 0; i < pheader->max_client_index; i++)
05467       if (pclient[i].pid) {
05468 #ifdef DEBUG_MSG
05469          cm_msg(MDEBUG, caller_name, "bm_update_read_pointer: client %d rp=%d", i, pclient[i].read_pointer);
05470 #endif
05471          bm_validate_client_pointers(pheader, &pclient[i]);
05472 
05473          if (pheader->read_pointer <= pheader->write_pointer) {
05474             if (pclient[i].read_pointer < min_rp)
05475                min_rp = pclient[i].read_pointer;
05476          } else {
05477             if (pclient[i].read_pointer <= pheader->write_pointer) {
05478                if (pclient[i].read_pointer < min_rp)
05479                   min_rp = pclient[i].read_pointer;
05480             } else {
05481                int xptr = pclient[i].read_pointer - pheader->size;
05482                if (xptr < min_rp)
05483                   min_rp = xptr;
05484             }
05485          }
05486       }
05487 
05488    if (min_rp < 0)
05489       min_rp += pheader->size;
05490 
05491    assert(min_rp >= 0);
05492    assert(min_rp < pheader->size);
05493 
05494 #ifdef DEBUG_MSG
05495    if (min_rp == pheader->read_pointer)
05496       cm_msg(MDEBUG, caller_name, "bm_update_read_pointer -> wp=%d", pheader->write_pointer);
05497    else
05498       cm_msg(MDEBUG, caller_name, "bm_update_read_pointer -> wp=%d, rp %d -> %d, size=%d",
05499              pheader->write_pointer, pheader->read_pointer, min_rp, pheader->size);
05500 #endif
05501 
05502    did_move = (pheader->read_pointer != min_rp);
05503 
05504    pheader->read_pointer = min_rp;
05505 
05506    return did_move;
05507 }
05508 
05509 static void bm_wakeup_producers(const BUFFER_HEADER * pheader, const BUFFER_CLIENT * pc)
05510 {
05511    int i;
05512    int size;
05513    const BUFFER_CLIENT *pctmp = pheader->client;
05514 
05515    /*
05516       If read pointer has been changed, it may have freed up some space
05517       for waiting producers. So check if free space is now more than 50%
05518       of the buffer size and wake waiting producers.
05519     */
05520 
05521    size = pc->read_pointer - pheader->write_pointer;
05522    if (size <= 0)
05523       size += pheader->size;
05524 
05525    if (size >= pheader->size * 0.5)
05526       for (i = 0; i < pheader->max_client_index; i++, pctmp++)
05527          if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||  /* check if not own thread */
05528                                                           (pctmp->pid == ss_getpid()
05529                                                            && pctmp->tid != ss_gettid()))) {
05530 #ifdef DEBUG_MSG
05531             cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
05532                    pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05533 #endif
05534             ss_resume(pctmp->port, "B  ");
05535          }
05536 }
05537 
05538 static void bm_dispatch_event(int buffer_handle, EVENT_HEADER * pevent)
05539 {
05540    int i;
05541 
05542    /* call dispatcher */
05543    for (i = 0; i < _request_list_entries; i++)
05544       if (_request_list[i].buffer_handle == buffer_handle &&
05545           bm_match_event(_request_list[i].event_id, _request_list[i].trigger_mask, pevent)) {
05546          /* if event is fragmented, call defragmenter */
05547          if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 || (pevent->event_id & 0xF000) == EVENTID_FRAG)
05548             bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), _request_list[i].dispatcher);
05549          else
05550             _request_list[i].dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
05551       }
05552 }
05553 
05554 static void bm_dispatch_from_cache(BUFFER * pbuf, int buffer_handle)
05555 {
05556    EVENT_HEADER *pevent;
05557    int size;
05558 
05559    pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
05560    size = pevent->data_size + sizeof(EVENT_HEADER);
05561 
05562    /* correct size for DWORD boundary */
05563    size = ALIGN8(size);
05564 
05565    /* increment read pointer */
05566    pbuf->read_cache_rp += size;
05567 
05568    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
05569       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05570 
05571    bm_dispatch_event(buffer_handle, pevent);
05572 }
05573 
05574 static void bm_convert_event_header(EVENT_HEADER * pevent, int convert_flags)
05575 {
05576    /* now convert event header */
05577    if (convert_flags) {
05578       rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
05579       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING, convert_flags);
05580       rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING, convert_flags);
05581       rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING, convert_flags);
05582       rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING, convert_flags);
05583    }
05584 }
05585 
05586 static int bm_copy_from_cache(BUFFER * pbuf, void *destination, int max_size,
05587                               int *buf_size, int convert_flags)
05588 {
05589    int status;
05590    EVENT_HEADER *pevent;
05591    int size;
05592 
05593    pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
05594    size = pevent->data_size + sizeof(EVENT_HEADER);
05595 
05596    if (size > max_size) {
05597       memcpy(destination, pevent, max_size);
05598       cm_msg(MERROR, "bm_receive_event", "event size %d larger than buffer size %d", size, max_size);
05599       *buf_size = max_size;
05600       status = BM_TRUNCATED;
05601    } else {
05602       memcpy(destination, pevent, size);
05603       *buf_size = size;
05604       status = BM_SUCCESS;
05605    }
05606 
05607    bm_convert_event_header((EVENT_HEADER *) destination, convert_flags);
05608 
05609    /* correct size for DWORD boundary */
05610    size = ALIGN8(size);
05611 
05612    pbuf->read_cache_rp += size;
05613 
05614    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
05615       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05616 
05617    return status;
05618 }
05619 
05620 static int bm_read_cache_has_events(const BUFFER * pbuf)
05621 {
05622    if (pbuf->read_cache_size == 0)
05623       return 0;
05624 
05625    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
05626       return 0;
05627 
05628    return 1;
05629 }
05630 
05631 static int bm_wait_for_free_space(int buffer_handle, BUFFER * pbuf, int async_flag, int requested_space)
05632 {
05633    int status;
05634    BUFFER_HEADER *pheader = pbuf->buffer_header;
05635    char *pdata = (char *) (pheader + 1);
05636 
05637    /* make sure the buffer never completely full:
05638     * read pointer and write pointer would coincide
05639     * and the code cannot tell if it means the
05640     * buffer is 100% or 100% empty. It will explode
05641     * or lose events */
05642    requested_space += 100;
05643 
05644    if (requested_space >= pheader->size)
05645       return BM_NO_MEMORY;
05646 
05647    while (1) {
05648 
05649       BUFFER_CLIENT *pc;
05650       int n_blocking;
05651       int i;
05652       int size;
05653 
05654       /* check if enough space in buffer */
05655 
05656       size = pheader->read_pointer - pheader->write_pointer;
05657       if (size <= 0)
05658          size += pheader->size;
05659 
05660 #if 0
05661       printf
05662           ("bm_send_event: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d\n",
05663            pheader->read_pointer, pheader->write_pointer, size, pheader->size, requested_space);
05664 #endif
05665 
05666       if (requested_space < size)       /* note the '<' to avoid 100% filling */
05667          return BM_SUCCESS;
05668 
05669       /* if not enough space, find out who's blocking */
05670       n_blocking = 0;
05671 
05672       for (i = 0, pc = pheader->client; i < pheader->max_client_index; i++, pc++)
05673          if (pc->pid) {
05674             if (pc->read_pointer == pheader->read_pointer) {
05675                /*
05676                   First assume that the client with the "minimum" read pointer
05677                   is not really blocking due to a GET_ALL request.
05678                 */
05679                BOOL blocking = FALSE;
05680                int blocking_request_id = 0;
05681                int j;
05682 
05683                /* check if this request blocks */
05684 
05685                EVENT_REQUEST *prequest = pc->event_request;
05686                EVENT_HEADER *pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
05687 
05688                for (j = 0; j < pc->max_request_index; j++, prequest++)
05689                   if (prequest->valid &&
05690                       bm_match_event(prequest->event_id, prequest->trigger_mask, pevent_test)) {
05691                      if (prequest->sampling_type & GET_ALL) {
05692                         blocking = TRUE;
05693                         blocking_request_id = prequest->id;
05694                         break;
05695                      }
05696                   }
05697 
05698                if (blocking) {
05699                   n_blocking++;
05700 
05701                   if (pc->read_wait) {
05702                      char str[80];
05703 #ifdef DEBUG_MSG
05704                      cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
05705 #endif
05706                      sprintf(str, "B %s %d", pheader->name, blocking_request_id);
05707                      ss_resume(pc->port, str);
05708                   }
05709 
05710                } else {
05711                   /*
05712                      The blocking guy has no GET_ALL request for this event
05713                      -> shift its read pointer.
05714                    */
05715 
05716                   int new_read_pointer;
05717                   int increment = sizeof(EVENT_HEADER) +
05718                       ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
05719 
05720                   /* correct increment for DWORD boundary */
05721                   increment = ALIGN8(increment);
05722 
05723                   new_read_pointer = (pc->read_pointer + increment) % pheader->size;
05724 
05725                   if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05726                      new_read_pointer = 0;
05727 
05728                   pc->read_pointer = new_read_pointer;
05729                }
05730             }
05731          }
05732       /* client loop */
05733       if (n_blocking == 0) {
05734 
05735          BOOL moved;
05736          /*
05737             calculate new global read pointer as "minimum" of
05738             client read pointers
05739           */
05740 
05741          moved = bm_update_read_pointer("bm_send_event", pheader);
05742 
05743          if (!moved) {
05744             cm_msg(MERROR, "bm_wait_for_free_space",
05745                    "BUG: read pointer did not move while waiting for %d bytes, bytes available: %d, buffer size: %d",
05746                    requested_space, size, pheader->size);
05747             return BM_NO_MEMORY;
05748          }
05749 
05750          continue;
05751       }
05752 
05753       /* at least one client is blocking */
05754 
05755       bm_unlock_buffer(buffer_handle);
05756 
05757       /* return now in ASYNC mode */
05758       if (async_flag)
05759          return BM_ASYNC_RETURN;
05760 
05761 #ifdef DEBUG_MSG
05762       cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
05763              pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05764 #endif
05765 
05766       /* signal other clients wait mode */
05767       pheader->client[bm_validate_client_index(pbuf)].write_wait = requested_space;
05768 
05769       status = ss_suspend(1000, MSG_BM);
05770 
05771       /* validate client index: we could have been removed from the buffer */
05772       pheader->client[bm_validate_client_index(pbuf)].write_wait = 0;
05773 
05774       /* return if TCP connection broken */
05775       if (status == SS_ABORT)
05776          return SS_ABORT;
05777 
05778 #ifdef DEBUG_MSG
05779       cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
05780              pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05781 #endif
05782 
05783       bm_lock_buffer(buffer_handle);
05784    }
05785 }
05786 
05787 /********************************************************************/
05788 /**
05789 Sends an event to a buffer.
05790 This function check if the buffer has enough space for the
05791 event, then copies the event to the buffer in shared memory.
05792 If clients have requests for the event, they are notified via an UDP packet.
05793 \code
05794 char event[1000];
05795 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05796 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05797 
05798 // set first byte of event
05799 *(event+sizeof(EVENT_HEADER)) = <...>
05800 #include <stdio.h>
05801 #include "midas.h"
05802 main()
05803 {
05804  INT status, i;
05805  HNDLE hbuf;
05806  char event[1000];
05807  status = cm_connect_experiment("", "Sample", "Producer", NULL);
05808  if (status != CM_SUCCESS)
05809  return 1;
05810  bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
05811 
05812  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05813  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05814 
05815  // set event data
05816  for (i=0 ; i<100 ; i++)
05817  *(event+sizeof(EVENT_HEADER)+i) = i;
05818  // send event
05819  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
05820  cm_disconnect_experiment();
05821  return 0;
05822 }
05823 \endcode
05824 @param buffer_handle Buffer handle obtained via bm_open_buffer()
05825 @param source Address of event buffer
05826 @param buf_size Size of event including event header in bytes
05827 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
05828 blocks if the buffer has not enough free space to receive the event.
05829 If TRUE, the function returns immediately with a
05830 value of BM_ASYNC_RETURN without writing the event to the buffer
05831 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
05832 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
05833 buffer has not enough space to receive event<br>
05834 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
05835 One has to increase MAX_EVENT_SIZE in midas.h and
05836 recompile.
05837 */
05838 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
05839 {
05840    EVENT_HEADER *pevent;
05841 
05842    /* check if event size defined in header matches buf_size */
05843    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
05844       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
05845              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER)));
05846       return BM_INVALID_PARAM;
05847    }
05848 
05849    /* check for maximal event size */
05850    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
05851       cm_msg(MERROR, "bm_send_event",
05852              "event size (%d) larger than maximum event size (%d)",
05853              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
05854       return BM_NO_MEMORY;
05855    }
05856 
05857    if (rpc_is_remote())
05858       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
05859 
05860 #ifdef LOCAL_ROUTINES
05861    {
05862       BUFFER *pbuf;
05863       BUFFER_HEADER *pheader;
05864       BUFFER_CLIENT *pclient;
05865       EVENT_REQUEST *prequest;
05866       INT i, j, size, total_size, status;
05867       INT my_client_index;
05868       INT old_write_pointer;
05869       INT num_requests_client;
05870       char *pdata;
05871       INT request_id;
05872 
05873       pbuf = &_buffer[buffer_handle - 1];
05874 
05875       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05876          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
05877          return BM_INVALID_HANDLE;
05878       }
05879 
05880       if (!pbuf->attached) {
05881          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
05882          return BM_INVALID_HANDLE;
05883       }
05884 
05885       pevent = (EVENT_HEADER *) source;
05886       total_size = buf_size;
05887 
05888       /* round up total_size to next DWORD boundary */
05889       total_size = ALIGN8(total_size);
05890 
05891       /* look if there is space in the cache */
05892       if (pbuf->write_cache_size) {
05893          status = BM_SUCCESS;
05894 
05895          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
05896             status = bm_flush_cache(buffer_handle, async_flag);
05897 
05898          if (status != BM_SUCCESS)
05899             return status;
05900 
05901          if (total_size < pbuf->write_cache_size) {
05902             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
05903 
05904             pbuf->write_cache_wp += total_size;
05905             return BM_SUCCESS;
05906          }
05907       }
05908 
05909       /* we come here only for events that are too big to fit into the cache */
05910 
05911       /* calculate some shorthands */
05912       pheader = pbuf->buffer_header;
05913       pdata = (char *) (pheader + 1);
05914       my_client_index = bm_validate_client_index(pbuf);
05915       pclient = pheader->client;
05916 
05917       /* check if buffer is large enough */
05918       if (total_size >= pheader->size) {
05919          cm_msg(MERROR, "bm_send_event",
05920                 "total event size (%d) larger than buffer size (%d)", total_size, pheader->size);
05921          return BM_NO_MEMORY;
05922       }
05923 
05924       /* lock the buffer */
05925       bm_lock_buffer(buffer_handle);
05926 
05927       status = bm_wait_for_free_space(buffer_handle, pbuf, async_flag, total_size);
05928       if (status != BM_SUCCESS) {
05929          bm_unlock_buffer(buffer_handle);
05930          return status;
05931       }
05932 
05933       /* we have space, so let's copy the event */
05934       old_write_pointer = pheader->write_pointer;
05935 
05936       if (pheader->write_pointer + total_size <= pheader->size) {
05937          memcpy(pdata + pheader->write_pointer, pevent, total_size);
05938          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
05939          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05940             pheader->write_pointer = 0;
05941       } else {
05942          /* split event */
05943          size = pheader->size - pheader->write_pointer;
05944 
05945          memcpy(pdata + pheader->write_pointer, pevent, size);
05946          memcpy(pdata, (char *) pevent + size, total_size - size);
05947 
05948          pheader->write_pointer = total_size - size;
05949       }
05950 
05951       /* write pointer was incremented, but there should
05952        * always be some free space in the buffer and the
05953        * write pointer should never cacth up to the read pointer:
05954        * the rest of the code gets confused this happens (buffer 100% full)
05955        * as it is write_pointer == read_pointer can be either
05956        * 100% full or 100% empty. My solution: never fill
05957        * the buffer to 100% */
05958       assert(pheader->write_pointer != pheader->read_pointer);
05959 
05960       /* check which clients have a request for this event */
05961       for (i = 0; i < pheader->max_client_index; i++)
05962          if (pclient[i].pid) {
05963             prequest = pclient[i].event_request;
05964             num_requests_client = 0;
05965             request_id = -1;
05966 
05967             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
05968                if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
05969                   if (prequest->sampling_type & GET_ALL)
05970                      pclient[i].num_waiting_events++;
05971 
05972                   num_requests_client++;
05973                   request_id = prequest->id;
05974                }
05975 
05976             /* if that client has a request and is suspended, wake it up */
05977             if (num_requests_client && pclient[i].read_wait) {
05978                char str[80];
05979 #ifdef DEBUG_MSG
05980                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
05981 #endif
05982                sprintf(str, "B %s %d", pheader->name, request_id);
05983                ss_resume(pclient[i].port, str);
05984             }
05985 
05986             /* if that client has no request, shift its read pointer */
05987             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
05988                pclient[i].read_pointer = pheader->write_pointer;
05989          }
05990 
05991       /* shift read pointer of own client */
05992       if (pclient[my_client_index].read_pointer == old_write_pointer)
05993          pclient[my_client_index].read_pointer = pheader->write_pointer;
05994 
05995       /* calculate global read pointer as "minimum" of client read pointers */
05996 
05997       bm_update_read_pointer("bm_send_event", pheader);
05998 
05999       /* update statistics */
06000       pheader->num_in_events++;
06001 
06002       /* unlock the buffer */
06003       bm_unlock_buffer(buffer_handle);
06004    }
06005 #endif                          /* LOCAL_ROUTINES */
06006 
06007    return BM_SUCCESS;
06008 }
06009 
06010 /********************************************************************/
06011 /**
06012 Empty write cache.
06013 This function should be used if events in the write cache
06014 should be visible to the consumers immediately. It should be called at the
06015 end of each run, otherwise events could be kept in the write buffer and will
06016 flow to the data of the next run.
06017 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06018 @param async_flag Synchronous/asynchronous flag.
06019 If FALSE, the function blocks if the buffer has not
06020 enough free space to receive the full cache. If TRUE, the function returns
06021 immediately with a value of BM_ASYNC_RETURN without writing the cache.
06022 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
06023 BM_ASYNC_RETURN Routine called with async_flag == TRUE
06024 and buffer has not enough space to receive cache<br>
06025 BM_NO_MEMORY Event is too large for network buffer or event buffer.
06026 One has to increase MAX_EVENT_SIZE in midas.h
06027 and recompile.
06028 */
06029 INT bm_flush_cache(INT buffer_handle, INT async_flag)
06030 {
06031    if (rpc_is_remote())
06032       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
06033 
06034 #ifdef LOCAL_ROUTINES
06035    {
06036       BUFFER *pbuf;
06037       BUFFER_HEADER *pheader;
06038       BUFFER_CLIENT *pclient;
06039       EVENT_HEADER *pevent;
06040       INT i, size, total_size, status;
06041       INT my_client_index;
06042       INT old_write_pointer;
06043       char *pdata;
06044 
06045       pbuf = &_buffer[buffer_handle - 1];
06046 
06047       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06048          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06049          return BM_INVALID_HANDLE;
06050       }
06051 
06052       if (!pbuf->attached) {
06053          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06054          return BM_INVALID_HANDLE;
06055       }
06056 
06057       if (pbuf->write_cache_size == 0)
06058          return BM_SUCCESS;
06059 
06060       /* check if anything needs to be flushed */
06061       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
06062          return BM_SUCCESS;
06063 
06064       /* calculate some shorthands */
06065       pheader = _buffer[buffer_handle - 1].buffer_header;
06066       pdata = (char *) (pheader + 1);
06067       my_client_index = bm_validate_client_index(pbuf);
06068       pclient = pheader->client;
06069       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06070 
06071       /* lock the buffer */
06072       bm_lock_buffer(buffer_handle);
06073 
06074 #ifdef DEBUG_MSG
06075       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06076 #endif
06077 
06078       status = bm_wait_for_free_space(buffer_handle, pbuf, async_flag, pbuf->write_cache_wp);
06079       if (status != BM_SUCCESS) {
06080          bm_unlock_buffer(buffer_handle);
06081          return status;
06082       }
06083 
06084       /* we have space, so let's copy the event */
06085       old_write_pointer = pheader->write_pointer;
06086 
06087 #ifdef DEBUG_MSG
06088       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
06089              pheader->write_pointer);
06090 #endif
06091 
06092       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06093          /* loop over all events in cache */
06094 
06095          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06096          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06097 
06098          assert(total_size > 0);
06099          assert(total_size <= pheader->size);
06100 
06101          /* correct size for DWORD boundary */
06102          total_size = ALIGN8(total_size);
06103 
06104          if (pheader->write_pointer + total_size <= pheader->size) {
06105             memcpy(pdata + pheader->write_pointer, pevent, total_size);
06106             pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06107             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06108                pheader->write_pointer = 0;
06109          } else {
06110             /* split event */
06111             size = pheader->size - pheader->write_pointer;
06112 
06113             memcpy(pdata + pheader->write_pointer, pevent, size);
06114             memcpy(pdata, (char *) pevent + size, total_size - size);
06115 
06116             pheader->write_pointer = total_size - size;
06117          }
06118 
06119          /* see comment for the same code in bm_send_event().
06120           * We make sure the buffer is nevere 100% full */
06121          assert(pheader->write_pointer != pheader->read_pointer);
06122 
06123          /* this loop does not loop forever because write_cache_rp
06124           * is monotonously incremented here. write_cache_wp does
06125           * not change */
06126 
06127          pbuf->write_cache_rp += total_size;
06128       }
06129 
06130       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06131 
06132       /* check which clients are waiting */
06133       for (i = 0; i < pheader->max_client_index; i++)
06134          if (pclient[i].pid && pclient[i].read_wait) {
06135             char str[80];
06136 #ifdef DEBUG_MSG
06137             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06138 #endif
06139             sprintf(str, "B %s %d", pheader->name, -1);
06140             ss_resume(pclient[i].port, str);
06141          }
06142 
06143       /* shift read pointer of own client */
06144       if (pclient[my_client_index].read_pointer == old_write_pointer)
06145          pclient[my_client_index].read_pointer = pheader->write_pointer;
06146 
06147       /* calculate global read pointer as "minimum" of client read pointers */
06148 
06149       bm_update_read_pointer("bm_flush_cache", pheader);
06150 
06151       /* update statistics */
06152       pheader->num_in_events++;
06153 
06154       /* unlock the buffer */
06155       bm_unlock_buffer(buffer_handle);
06156    }
06157 #endif                          /* LOCAL_ROUTINES */
06158 
06159    return BM_SUCCESS;
06160 }
06161 
06162 /********************************************************************/
06163 /**
06164 Receives events directly.
06165 This function is an alternative way to receive events without
06166 a main loop.
06167 
06168 It can be used in analysis systems which actively receive events,
06169 rather than using callbacks. A analysis package could for example contain its own
06170 command line interface. A command
06171 like "receive 1000 events" could make it necessary to call bm_receive_event()
06172 1000 times in a row to receive these events and then return back to the
06173 command line prompt.
06174 The according bm_request_event() call contains NULL as the
06175 callback routine to indicate that bm_receive_event() is called to receive
06176 events.
06177 \code
06178 #include <stdio.h>
06179 #include "midas.h"
06180 void process_event(EVENT_HEADER *pheader)
06181 {
06182  printf("Received event #%d\r",
06183  pheader->serial_number);
06184 }
06185 main()
06186 {
06187   INT status, request_id;
06188   HNDLE hbuf;
06189   char event_buffer[1000];
06190   status = cm_connect_experiment("", "Sample",
06191   "Simple Analyzer", NULL);
06192   if (status != CM_SUCCESS)
06193    return 1;
06194   bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
06195   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06196 
06197   do
06198   {
06199    size = sizeof(event_buffer);
06200    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06201   if (status == CM_SUCCESS)
06202    process_event((EVENT_HEADER *) event_buffer);
06203    <...do something else...>
06204    status = cm_yield(0);
06205   } while (status != RPC_SHUTDOWN &&
06206   status != SS_ABORT);
06207   cm_disconnect_experiment();
06208   return 0;
06209 }
06210 \endcode
06211 @param buffer_handle buffer handle
06212 @param destination destination address where event is written to
06213 @param buf_size size of destination buffer on input, size of event plus
06214 header on return.
06215 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06216 blocks if no event is available. If TRUE, the function returns immediately
06217 with a value of BM_ASYNC_RETURN without receiving any event.
06218 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06219 BM_TRUNCATED   The event is larger than the destination buffer and was
06220                therefore truncated <br>
06221 BM_ASYNC_RETURN No event available
06222 */
06223 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06224 {
06225    if (rpc_is_remote()) {
06226       int status, old_timeout = 0;
06227 
06228       if (*buf_size > NET_BUFFER_SIZE) {
06229          cm_msg(MERROR, "bm_receive_event", "max. event size larger than NET_BUFFER_SIZE");
06230          return RPC_NET_ERROR;
06231       }
06232 
06233       if (!async_flag) {
06234          old_timeout = rpc_get_option(-1, RPC_OTIMEOUT);
06235          rpc_set_option(-1, RPC_OTIMEOUT, 0);
06236       }
06237 
06238       status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, destination, buf_size, async_flag);
06239 
06240       if (!async_flag) {
06241          rpc_set_option(-1, RPC_OTIMEOUT, old_timeout);
06242       }
06243 
06244       return status;
06245    }
06246 #ifdef LOCAL_ROUTINES
06247    {
06248       BUFFER *pbuf;
06249       BUFFER_HEADER *pheader;
06250       BUFFER_CLIENT *pclient, *pc;
06251       char *pdata;
06252       INT convert_flags;
06253       INT i, size, max_size;
06254       INT status = 0;
06255       INT my_client_index;
06256       BOOL cache_is_full = FALSE;
06257       BOOL use_event_buffer = FALSE;
06258       int cycle = 0;
06259 
06260       pbuf = &_buffer[buffer_handle - 1];
06261 
06262       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06263          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06264          return BM_INVALID_HANDLE;
06265       }
06266 
06267       if (!pbuf->attached) {
06268          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06269          return BM_INVALID_HANDLE;
06270       }
06271 
06272       max_size = *buf_size;
06273       *buf_size = 0;
06274 
06275       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06276          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06277       else
06278          convert_flags = 0;
06279 
06280       /* look if there is anything in the cache */
06281       if (bm_read_cache_has_events(pbuf))
06282          return bm_copy_from_cache(pbuf, destination, max_size, buf_size, convert_flags);
06283 
06284     LOOP:
06285       /* make sure we do bm_validate_client_index() after sleeping */
06286 
06287       /* calculate some shorthands */
06288       pheader = pbuf->buffer_header;
06289       pdata = (char *) (pheader + 1);
06290       my_client_index = bm_validate_client_index(pbuf);
06291       pclient = pheader->client;
06292       pc = pheader->client + my_client_index;
06293 
06294       /* first do a quick check without locking the buffer */
06295       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06296          return BM_ASYNC_RETURN;
06297 
06298       /* lock the buffer */
06299       bm_lock_buffer(buffer_handle);
06300 
06301       while (pheader->write_pointer == pc->read_pointer) {
06302 
06303          bm_unlock_buffer(buffer_handle);
06304 
06305          /* return now in ASYNC mode */
06306          if (async_flag == ASYNC)
06307             return BM_ASYNC_RETURN;
06308 
06309          pc->read_wait = TRUE;
06310 
06311          /* check again pointers (may have moved in between) */
06312          if (pheader->write_pointer == pc->read_pointer) {
06313 #ifdef DEBUG_MSG
06314             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06315                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06316 #endif
06317 
06318             status = ss_suspend(1000, MSG_BM);
06319 
06320 #ifdef DEBUG_MSG
06321             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06322 #endif
06323 
06324             /* return if TCP connection broken */
06325             if (status == SS_ABORT)
06326                return SS_ABORT;
06327          }
06328 
06329          pc->read_wait = FALSE;
06330 
06331          /* validate client_index: somebody may have disconnected us from the buffer */
06332          bm_validate_client_index(pbuf);
06333 
06334          bm_lock_buffer(buffer_handle);
06335       }
06336 
06337       /* check if event at current read pointer matches a request */
06338 
06339       do {
06340          int new_read_pointer;
06341          int total_size;        /* size of the event */
06342          EVENT_REQUEST *prequest;
06343          EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06344 
06345          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06346          total_size = ALIGN8(total_size);
06347 
06348          assert(total_size > 0);
06349          assert(total_size <= pheader->size);
06350 
06351          prequest = pc->event_request;
06352 
06353          /* loop over all requests: if this event matches a request,
06354           * copy it to the read cache */
06355 
06356          for (i = 0; i < pc->max_request_index; i++, prequest++)
06357             if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06358 
06359                /* we found a request for this event, so copy it */
06360 
06361                if (pbuf->read_cache_size > 0 && total_size < pbuf->read_cache_size) {
06362 
06363                   /* copy event to cache, if there is room */
06364 
06365                   if (pbuf->read_cache_wp + total_size >= pbuf->read_cache_size) {
06366                      cache_is_full = TRUE;
06367                      break;     /* exit loop over requests */
06368                   }
06369 
06370                   if (pc->read_pointer + total_size <= pheader->size) {
06371                      /* copy event to cache */
06372                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06373                   } else {
06374                      /* event is splitted */
06375                      size = pheader->size - pc->read_pointer;
06376                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06377                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size, pdata, total_size - size);
06378                   }
06379 
06380                   pbuf->read_cache_wp += total_size;
06381 
06382                } else {
06383                   int copy_size = total_size;
06384 
06385                   /* if there are events in the read cache,
06386                    * we should dispatch them before we
06387                    * despatch this oversize event */
06388 
06389                   if (bm_read_cache_has_events(pbuf)) {
06390                      cache_is_full = TRUE;
06391                      break;     /* exit loop over requests */
06392                   }
06393 
06394                   use_event_buffer = TRUE;
06395 
06396                   status = BM_SUCCESS;
06397                   if (copy_size > max_size) {
06398                      copy_size = max_size;
06399                      cm_msg(MERROR, "bm_receive_event",
06400                             "event size %d larger than buffer size %d", total_size, max_size);
06401                      status = BM_TRUNCATED;
06402                   }
06403 
06404                   if (pc->read_pointer + total_size <= pheader->size) {
06405                      /* event is not splitted */
06406                      memcpy(destination, pevent, copy_size);
06407                   } else {
06408                      /* event is splitted */
06409                      size = pheader->size - pc->read_pointer;
06410 
06411                      if (size > max_size)
06412                         memcpy(destination, pevent, max_size);
06413                      else
06414                         memcpy(destination, pevent, size);
06415 
06416                      if (total_size > max_size) {
06417                         if (size <= max_size)
06418                            memcpy((char *) destination + size, pdata, max_size - size);
06419                      } else
06420                         memcpy((char *) destination + size, pdata, total_size - size);
06421                   }
06422 
06423                   *buf_size = copy_size;
06424 
06425                   bm_convert_event_header((EVENT_HEADER *) destination, convert_flags);
06426                }
06427 
06428                /* update statistics */
06429                pheader->num_out_events++;
06430                break;           /* stop looping over requests */
06431             }
06432 
06433          if (cache_is_full)
06434             break;              /* exit from loop over events in data buffer, leaving the current event untouched */
06435 
06436          /* shift read pointer */
06437 
06438          new_read_pointer = pc->read_pointer + total_size;
06439          if (new_read_pointer >= pheader->size) {
06440             new_read_pointer = new_read_pointer % pheader->size;
06441 
06442             /* make sure we loop over the data buffer no more than once */
06443             cycle++;
06444             assert(cycle < 2);
06445          }
06446 
06447          /* make sure we do not split the event header at the end of the buffer */
06448          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06449             new_read_pointer = 0;
06450 
06451 #ifdef DEBUG_MSG
06452          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06453                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found, total_size);
06454 #endif
06455 
06456          pc->read_pointer = new_read_pointer;
06457 
06458          if (use_event_buffer)
06459             break;              /* exit from loop over events in data buffer */
06460 
06461       } while (pheader->write_pointer != pc->read_pointer);
06462 
06463       /* calculate global read pointer as "minimum" of client read pointers */
06464 
06465       bm_update_read_pointer("bm_receive_event", pheader);
06466 
06467       /*
06468          If read pointer has been changed, it may have freed up some space
06469          for waiting producers. So check if free space is now more than 50%
06470          of the buffer size and wake waiting producers.
06471        */
06472 
06473       bm_wakeup_producers(pheader, pc);
06474 
06475       bm_unlock_buffer(buffer_handle);
06476 
06477       if (bm_read_cache_has_events(pbuf)) {
06478          assert(!use_event_buffer);     /* events only go into the _event_buffer when read cache is empty */
06479          return bm_copy_from_cache(pbuf, destination, max_size, buf_size, convert_flags);
06480       }
06481 
06482       if (use_event_buffer)
06483          return status;
06484 
06485       goto LOOP;
06486    }
06487 #else                           /* LOCAL_ROUTINES */
06488 
06489    return SS_SUCCESS;
06490 #endif
06491 }
06492 
06493 /********************************************************************/
06494 /**
06495 Skip all events in current buffer.
06496 
06497 Useful for single event displays to see the newest events
06498 @param buffer_handle      Handle of the buffer. Must be obtained
06499                           via bm_open_buffer.
06500 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR
06501 */
06502 INT bm_skip_event(INT buffer_handle)
06503 {
06504    if (rpc_is_remote())
06505       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
06506 
06507 #ifdef LOCAL_ROUTINES
06508    {
06509       BUFFER *pbuf;
06510       BUFFER_HEADER *pheader;
06511       BUFFER_CLIENT *pclient;
06512 
06513       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06514          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06515          return BM_INVALID_HANDLE;
06516       }
06517 
06518       pbuf = &_buffer[buffer_handle - 1];
06519       pheader = pbuf->buffer_header;
06520 
06521       if (!pbuf->attached) {
06522          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06523          return BM_INVALID_HANDLE;
06524       }
06525 
06526       /* clear cache */
06527       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
06528          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06529 
06530       bm_lock_buffer(buffer_handle);
06531 
06532       /* forward read pointer to global write pointer */
06533       pclient = pheader->client + bm_validate_client_index(pbuf);
06534       pclient->read_pointer = pheader->write_pointer;
06535 
06536       bm_unlock_buffer(buffer_handle);
06537    }
06538 #endif
06539 
06540    return BM_SUCCESS;
06541 }
06542 
06543 /********************************************************************/
06544 /**
06545 Check a buffer if an event is available and call the dispatch function if found.
06546 @param buffer_name       Name of buffer
06547 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN,
06548                     RPC_NET_ERROR
06549 */
06550 INT bm_push_event(char *buffer_name)
06551 {
06552 #ifdef LOCAL_ROUTINES
06553    {
06554       BUFFER *pbuf;
06555       BUFFER_HEADER *pheader;
06556       BUFFER_CLIENT *pclient, *pc;
06557       char *pdata;
06558       INT i, size, buffer_handle;
06559       INT my_client_index;
06560       BOOL use_event_buffer = 0;
06561       BOOL cache_is_full = 0;
06562       int cycle = 0;
06563 
06564       for (i = 0; i < _buffer_entries; i++)
06565          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
06566             break;
06567       if (i == _buffer_entries)
06568          return BM_INVALID_HANDLE;
06569 
06570       buffer_handle = i + 1;
06571       pbuf = &_buffer[buffer_handle - 1];
06572 
06573       if (!pbuf->attached)
06574          return BM_INVALID_HANDLE;
06575 
06576       /* return immediately if no callback routine is defined */
06577       if (!pbuf->callback)
06578          return BM_SUCCESS;
06579 
06580       if (_event_buffer_size == 0) {
06581          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
06582          if (_event_buffer == NULL) {
06583             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
06584             return BM_NO_MEMORY;
06585          }
06586          _event_buffer_size = 1000;
06587       }
06588 
06589       /* look if there is anything in the cache */
06590       if (bm_read_cache_has_events(pbuf)) {
06591          bm_dispatch_from_cache(pbuf, buffer_handle);
06592          return BM_MORE_EVENTS;
06593       }
06594 
06595       /* calculate some shorthands */
06596       pheader = pbuf->buffer_header;
06597       pdata = (char *) (pheader + 1);
06598       my_client_index = bm_validate_client_index(pbuf);
06599       pclient = pheader->client;
06600       pc = pheader->client + my_client_index;
06601 
06602       /* first do a quick check without locking the buffer */
06603       if (pheader->write_pointer == pc->read_pointer)
06604          return BM_SUCCESS;
06605 
06606       /* lock the buffer */
06607       bm_lock_buffer(buffer_handle);
06608 
06609       if (pheader->write_pointer == pc->read_pointer) {
06610 
06611          bm_unlock_buffer(buffer_handle);
06612 
06613          /* return if no event available */
06614          return BM_SUCCESS;
06615       }
06616 
06617       /* loop over all events in the buffer */
06618 
06619       do {
06620          int new_read_pointer;
06621          int total_size;        /* size of the event */
06622          EVENT_REQUEST *prequest;
06623          EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06624 
06625          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06626          total_size = ALIGN8(total_size);
06627 
06628          assert(total_size > 0);
06629          assert(total_size <= pheader->size);
06630 
06631          prequest = pc->event_request;
06632 
06633          /* loop over all requests: if this event matches a request,
06634           * copy it to the read cache */
06635 
06636          for (i = 0; i < pc->max_request_index; i++, prequest++)
06637             if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06638 
06639                /* we found a request for this event, so copy it */
06640 
06641                if (pbuf->read_cache_size > 0 && total_size < pbuf->read_cache_size) {
06642 
06643                   /* copy event to cache, if there is room */
06644 
06645                   if (pbuf->read_cache_wp + total_size >= pbuf->read_cache_size) {
06646                      cache_is_full = TRUE;
06647                      break;     /* exit loop over requests */
06648                   }
06649 
06650                   if (pc->read_pointer + total_size <= pheader->size) {
06651                      /* copy event to cache */
06652                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06653                   } else {
06654                      /* event is splitted */
06655                      size = pheader->size - pc->read_pointer;
06656                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06657                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size, pdata, total_size - size);
06658                   }
06659 
06660                   pbuf->read_cache_wp += total_size;
06661 
06662                } else {
06663                   /* copy event to copy buffer */
06664 
06665                   /* if there are events in the read cache,
06666                    * we should dispatch them before we
06667                    * despatch this oversize event */
06668 
06669                   if (bm_read_cache_has_events(pbuf)) {
06670                      cache_is_full = TRUE;
06671                      break;     /* exit loop over requests */
06672                   }
06673 
06674                   use_event_buffer = TRUE;
06675 
06676                   if (total_size > _event_buffer_size) {
06677                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
06678                      _event_buffer_size = total_size;
06679                   }
06680 
06681                   if (pc->read_pointer + total_size <= pheader->size) {
06682                      memcpy(_event_buffer, pevent, total_size);
06683                   } else {
06684                      /* event is splitted */
06685                      size = pheader->size - pc->read_pointer;
06686 
06687                      memcpy(_event_buffer, pevent, size);
06688                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
06689                   }
06690                }
06691 
06692                /* update statistics */
06693                pheader->num_out_events++;
06694                break;           /* exit loop over event requests */
06695 
06696             }
06697          /* end of loop over event requests */
06698          if (cache_is_full)
06699             break;              /* exit from loop over events in data buffer, leaving the current event untouched */
06700 
06701          /* shift read pointer */
06702 
06703          new_read_pointer = pc->read_pointer + total_size;
06704          if (new_read_pointer >= pheader->size) {
06705             new_read_pointer = new_read_pointer % pheader->size;
06706 
06707             /* make sure we loop over the data buffer no more than once */
06708             cycle++;
06709             assert(cycle < 2);
06710          }
06711 
06712          /* make sure we do not split the event header at the end of the buffer */
06713          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06714             new_read_pointer = 0;
06715 
06716 #ifdef DEBUG_MSG
06717          cm_msg(MDEBUG, "bm_push_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06718                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found, total_size);
06719 #endif
06720 
06721          pc->read_pointer = new_read_pointer;
06722 
06723          if (use_event_buffer)
06724             break;              /* exit from loop over events in data buffer */
06725 
06726       } while (pheader->write_pointer != pc->read_pointer);
06727 
06728       /* calculate global read pointer as "minimum" of client read pointers */
06729 
06730       bm_update_read_pointer("bm_push_event", pheader);
06731 
06732       /*
06733          If read pointer has been changed, it may have freed up some space
06734          for waiting producers. So check if free space is now more than 50%
06735          of the buffer size and wake waiting producers.
06736        */
06737 
06738       bm_wakeup_producers(pheader, pc);
06739 
06740       bm_unlock_buffer(buffer_handle);
06741 
06742       if (bm_read_cache_has_events(pbuf)) {
06743          assert(!use_event_buffer);     /* events only go into the _event_buffer when read cache is empty */
06744          bm_dispatch_from_cache(pbuf, buffer_handle);
06745          return BM_MORE_EVENTS;
06746       }
06747 
06748       if (use_event_buffer) {
06749          bm_dispatch_event(buffer_handle, _event_buffer);
06750          return BM_MORE_EVENTS;
06751       }
06752 
06753       return BM_SUCCESS;
06754    }
06755 #else                           /* LOCAL_ROUTINES */
06756 
06757    return BM_SUCCESS;
06758 #endif
06759 }
06760 
06761 /********************************************************************/
06762 /**
06763 Check if any requested event is waiting in a buffer
06764 @return TRUE             More events are waiting<br>
06765         FALSE            No more events are waiting
06766 */
06767 INT bm_check_buffers()
06768 {
06769 #ifdef LOCAL_ROUTINES
06770    {
06771       INT index, status = 0;
06772       INT server_type, server_conn, tid;
06773       BOOL bMore;
06774       DWORD start_time;
06775 
06776       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
06777       server_conn = rpc_get_server_acception();
06778       tid = ss_gettid();
06779 
06780       /* if running as a server, buffer checking is done by client
06781          via ASYNC bm_receive_event */
06782       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
06783          return FALSE;
06784 
06785       bMore = FALSE;
06786       start_time = ss_millitime();
06787 
06788       /* go through all buffers */
06789       for (index = 0; index < _buffer_entries; index++) {
06790          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
06791             continue;
06792 
06793          if (server_type != ST_SINGLE && _buffer[index].index != tid)
06794             continue;
06795 
06796          if (!_buffer[index].attached)
06797             continue;
06798 
06799          do {
06800 
06801             /* one bm_push_event could cause a run stop and a buffer close, which
06802                would crash the next call to bm_push_event(). So check for valid
06803                buffer on each call */
06804             if (index < _buffer_entries && _buffer[index].buffer_header->name != NULL)
06805                status = bm_push_event(_buffer[index].buffer_header->name);
06806 
06807             if (status != BM_MORE_EVENTS)
06808                break;
06809 
06810             /* stop after one second */
06811             if (ss_millitime() - start_time > 1000) {
06812                bMore = TRUE;
06813                break;
06814             }
06815 
06816          } while (TRUE);
06817       }
06818 
06819       return bMore;
06820 
06821    }
06822 #else                           /* LOCAL_ROUTINES */
06823 
06824    return FALSE;
06825 
06826 #endif
06827 }
06828 
06829 /**dox***************************************************************/
06830 #ifndef DOXYGEN_SHOULD_SKIP_THIS
06831 
06832 /********************************************************************/
06833 INT bm_mark_read_waiting(BOOL flag)
06834 /********************************************************************\
06835 
06836   Routine: bm_mark_read_waiting
06837 
06838   Purpose: Mark all open buffers ready for receiving events.
06839            Called internally by ss_suspend
06840 
06841 
06842   Input:
06843     BOOL flag               TRUE for waiting, FALSE for not waiting
06844 
06845   Output:
06846     none
06847 
06848   Function value:
06849     BM_SUCCESS              Successful completion
06850 
06851 \********************************************************************/
06852 {
06853    if (rpc_is_remote())
06854       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
06855 
06856 #ifdef LOCAL_ROUTINES
06857    {
06858       INT i;
06859       BUFFER_HEADER *pheader;
06860       BUFFER_CLIENT *pclient;
06861 
06862       /* Mark all buffer for read waiting */
06863       for (i = 0; i < _buffer_entries; i++) {
06864          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
06865              _buffer[i].index != rpc_get_server_acception())
06866             continue;
06867 
06868          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i].index != ss_gettid())
06869             continue;
06870 
06871          if (!_buffer[i].attached)
06872             continue;
06873 
06874          pheader = _buffer[i].buffer_header;
06875          pclient = pheader->client + bm_validate_client_index(&_buffer[i]);
06876          pclient->read_wait = flag;
06877       }
06878    }
06879 #endif                          /* LOCAL_ROUTINES */
06880 
06881    return BM_SUCCESS;
06882 }
06883 
06884 /********************************************************************/
06885 INT bm_notify_client(char *buffer_name, int socket)
06886 /********************************************************************\
06887 
06888   Routine: bm_notify_client
06889 
06890   Purpose: Called by cm_dispatch_ipc. Send an event notification to
06891            the connected client
06892 
06893   Input:
06894     char  *buffer_name      Name of buffer
06895     int   socket            Network socket to client
06896 
06897   Output:
06898     none
06899 
06900   Function value:
06901     BM_SUCCESS              Successful completion
06902 
06903 \********************************************************************/
06904 {
06905    char buffer[32];
06906    NET_COMMAND *nc;
06907    INT i, convert_flags;
06908    static DWORD last_time = 0;
06909 
06910    for (i = 0; i < _buffer_entries; i++)
06911       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
06912          break;
06913    if (i == _buffer_entries)
06914       return BM_INVALID_HANDLE;
06915 
06916    /* don't send notification if client has no callback defined
06917       to receive events -> client calls bm_receive_event manually */
06918    if (!_buffer[i].callback)
06919       return DB_SUCCESS;
06920 
06921    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06922 
06923    /* only send notification once each 500ms */
06924    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
06925       return DB_SUCCESS;
06926 
06927    last_time = ss_millitime();
06928 
06929    if (convert_flags & CF_ASCII) {
06930       sprintf(buffer, "MSG_BM&%s", buffer_name);
06931       send_tcp(socket, buffer, strlen(buffer) + 1, 0);
06932    } else {
06933       nc = (NET_COMMAND *) buffer;
06934 
06935       nc->header.routine_id = MSG_BM;
06936       nc->header.param_size = 0;
06937 
06938       if (convert_flags) {
06939          rpc_convert_single(&nc->header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
06940          rpc_convert_single(&nc->header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
06941       }
06942 
06943       /* send the update notification to the client */
06944       send_tcp(socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
06945    }
06946 
06947    return BM_SUCCESS;
06948 }
06949 
06950 /********************************************************************/
06951 INT bm_poll_event(INT flag)
06952 /********************************************************************\
06953 
06954   Routine: bm_poll_event
06955 
06956   Purpose: Poll an event from a remote server. Gets called by
06957            rpc_client_dispatch
06958 
06959   Input:
06960     INT flag         TRUE if called from cm_yield
06961 
06962   Output:
06963     none
06964 
06965   Function value:
06966     TRUE             More events are waiting
06967     FALSE            No more events are waiting
06968     SS_ABORT         Network connection broken
06969 
06970 \********************************************************************/
06971 {
06972    INT status, size, i, request_id;
06973    DWORD start_time;
06974    BOOL bMore;
06975    static BOOL bMoreLast = FALSE;
06976 
06977    if (_event_buffer_size == 0) {
06978       _event_buffer = (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
06979       if (!_event_buffer) {
06980          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer");
06981          return SS_ABORT;
06982       }
06983       _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
06984    }
06985 
06986    start_time = ss_millitime();
06987 
06988    /* if we got event notification, turn off read_wait */
06989    if (!flag)
06990       bm_mark_read_waiting(FALSE);
06991 
06992    /* if called from yield, return if no more events */
06993    if (flag) {
06994       if (!bMoreLast)
06995          return FALSE;
06996    }
06997 
06998    bMore = FALSE;
06999 
07000    /* loop over all requests */
07001    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07002       /* continue if no dispatcher set (manual bm_receive_event) */
07003       if (_request_list[request_id].dispatcher == NULL)
07004          continue;
07005 
07006       do {
07007          /* receive event */
07008          size = _event_buffer_size;
07009          status = bm_receive_event(_request_list[request_id].buffer_handle, _event_buffer, &size, ASYNC);
07010 
07011          /* call user function if successful */
07012          if (status == BM_SUCCESS)
07013             /* search proper request for this event */
07014             for (i = 0; i < _request_list_entries; i++)
07015                if ((_request_list[i].buffer_handle ==
07016                     _request_list[request_id].buffer_handle) &&
07017                    bm_match_event(_request_list[i].event_id, _request_list[i].trigger_mask, _event_buffer)) {
07018                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07019                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07020                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07021                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07022                                          _request_list[i].dispatcher);
07023                   else
07024                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07025                                                  _event_buffer,
07026                                                  (void *) (((EVENT_HEADER *) _event_buffer) + 1));
07027                }
07028 
07029          /* break if no more events */
07030          if (status == BM_ASYNC_RETURN)
07031             break;
07032 
07033          /* break if server died */
07034          if (status == RPC_NET_ERROR)
07035             return SS_ABORT;
07036 
07037          /* stop after one second */
07038          if (ss_millitime() - start_time > 1000) {
07039             bMore = TRUE;
07040             break;
07041          }
07042 
07043       } while (TRUE);
07044    }
07045 
07046    if (!bMore)
07047       bm_mark_read_waiting(TRUE);
07048 
07049    bMoreLast = bMore;
07050 
07051    return bMore;
07052 }
07053 
07054 /**dox***************************************************************/
07055 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07056 
07057 /********************************************************************/
07058 /**
07059 Clears event buffer and cache.
07060 If an event buffer is large and a consumer is slow in analyzing
07061 events, events are usually received some time after they are produced.
07062 This effect is even more experienced if a read cache is used
07063 (via bm_set_cache_size()).
07064 When changes to the hardware are made in the experience, the consumer will then
07065 still analyze old events before any new event which reflects the hardware change.
07066 Users can be fooled by looking at histograms which reflect the hardware change
07067 many seconds after they have been made.
07068 
07069 To overcome this potential problem, the analyzer can call
07070 bm_empty_buffers() just after the hardware change has been made which
07071 skips all old events contained in event buffers and read caches.
07072 Technically this is done by forwarding the read pointer of the client.
07073 No events are really deleted, they are still visible to other clients like
07074 the logger.
07075 
07076 Note that the front-end also contains write buffers which can delay the
07077 delivery of events.
07078 The standard front-end framework mfe.c reduces this effect by flushing
07079 all buffers once every second.
07080 @return BM_SUCCESS
07081 */
07082 INT bm_empty_buffers()
07083 {
07084    if (rpc_is_remote())
07085       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07086 
07087 #ifdef LOCAL_ROUTINES
07088    {
07089       INT index, server_type, server_conn, tid;
07090       BUFFER *pbuf;
07091       BUFFER_CLIENT *pclient;
07092 
07093       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07094       server_conn = rpc_get_server_acception();
07095       tid = ss_gettid();
07096 
07097       /* go through all buffers */
07098       for (index = 0; index < _buffer_entries; index++) {
07099          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07100             continue;
07101 
07102          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07103             continue;
07104 
07105          if (!_buffer[index].attached)
07106             continue;
07107 
07108          pbuf = &_buffer[index];
07109 
07110          /* empty cache */
07111          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07112 
07113          /* set read pointer to write pointer */
07114          pclient = (pbuf->buffer_header)->client + bm_validate_client_index(pbuf);
07115          bm_lock_buffer(index + 1);
07116          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07117          bm_unlock_buffer(index + 1);
07118       }
07119 
07120    }
07121 #endif                          /* LOCAL_ROUTINES */
07122 
07123    return BM_SUCCESS;
07124 }
07125 
07126 /**dox***************************************************************/
07127 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07128 
07129 #define MAX_DEFRAG_EVENTS 10
07130 
07131 typedef struct {
07132    WORD event_id;
07133    DWORD data_size;
07134    DWORD received;
07135    EVENT_HEADER *pevent;
07136 } EVENT_DEFRAG_BUFFER;
07137 
07138 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07139 
07140 /********************************************************************/
07141 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07142                          EVENT_HEADER * pevent, void *pdata,
07143                          void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *, void *))
07144 /********************************************************************\
07145 
07146   Routine: bm_defragment_event
07147 
07148   Purpose: Called internally from the event receiving routines
07149            bm_push_event and bm_poll_event to recombine event
07150            fragments and call the user callback routine upon
07151            completion.
07152 
07153   Input:
07154     HNDLE buffer_handle  Handle for the buffer containing event
07155     HNDLE request_id     Handle for event request
07156     EVENT_HEADER *pevent Pointer to event header
07157     void *pata           Pointer to event data
07158     dispatcher()         User callback routine
07159 
07160   Output:
07161     <calls dispatcher() after successfull recombination of event>
07162 
07163   Function value:
07164     void
07165 
07166 \********************************************************************/
07167 {
07168    INT i;
07169    static int j = -1;
07170 
07171    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07172       /*---- start new event ----*/
07173 
07174       //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
07175 
07176       /* check if fragments already stored */
07177       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07178          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07179             break;
07180 
07181       if (i < MAX_DEFRAG_EVENTS) {
07182          free(defrag_buffer[i].pevent);
07183          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07184          cm_msg(MERROR, "bm_defragement_event",
07185                 "Received new event with ID %d while old fragments were not completed",
07186                 (pevent->event_id & 0x0FFF));
07187       }
07188 
07189       /* search new slot */
07190       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07191          if (defrag_buffer[i].event_id == 0)
07192             break;
07193 
07194       if (i == MAX_DEFRAG_EVENTS) {
07195          cm_msg(MERROR, "bm_defragment_event",
07196                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07197          return;
07198       }
07199 
07200       /* check event size */
07201       if (pevent->data_size != sizeof(DWORD)) {
07202          cm_msg(MERROR, "bm_defragment_event",
07203                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07204                 pevent->data_size, sizeof(DWORD));
07205          return;
07206       }
07207 
07208       /* setup defragment buffer */
07209       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07210       defrag_buffer[i].data_size = *(DWORD *) pdata;
07211       defrag_buffer[i].received = 0;
07212       defrag_buffer[i].pevent = (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07213 
07214       if (defrag_buffer[i].pevent == NULL) {
07215          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07216          cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
07217          return;
07218       }
07219 
07220       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07221       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07222       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07223 
07224       // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
07225       //       pevent->serial_number, defrag_buffer[i].data_size);
07226 
07227       j = 0;
07228 
07229       return;
07230    }
07231 
07232    /* search buffer for that event */
07233    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07234       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07235          break;
07236 
07237    if (i == MAX_DEFRAG_EVENTS) {
07238       /* no buffer available -> no first fragment received */
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 fragment without first fragment (ID %d) Ser#:%d",
07243              pevent->event_id & 0x0FFF, pevent->serial_number);
07244       return;
07245    }
07246 
07247    /* add fragment to buffer */
07248    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
07249       free(defrag_buffer[i].pevent);
07250       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07251       cm_msg(MERROR, "bm_defragement_event",
07252              "Received fragments with more data (%d) than event size (%d)",
07253              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
07254       return;
07255    }
07256 
07257    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07258           defrag_buffer[i].received, pdata, pevent->data_size);
07259 
07260    defrag_buffer[i].received += pevent->data_size;
07261 
07262    //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
07263    //       defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
07264 
07265    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
07266       /* event complete */
07267       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
07268       free(defrag_buffer[i].pevent);
07269       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07270    }
07271 }
07272 
07273 /**dox***************************************************************/
07274 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07275 
07276 /**dox***************************************************************/
07277                                                                                                              /** @} *//* end of bmfunctionc */
07278 
07279 /**dox***************************************************************/
07280 /** @addtogroup rpcfunctionc
07281  *
07282  *  @{  */
07283 
07284 /**dox***************************************************************/
07285 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07286 
07287 /********************************************************************\
07288 *                                                                    *
07289 *                         RPC functions                              *
07290 *                                                                    *
07291 \********************************************************************/
07292 
07293 /* globals */
07294 
07295 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
07296 RPC_SERVER_CONNECTION _server_connection;
07297 
07298 static int _lsock;
07299 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
07300 static INT _server_acception_index = 0;
07301 static INT _server_type;
07302 static char _server_name[256];
07303 
07304 static RPC_LIST *rpc_list = NULL;
07305 
07306 int _opt_tcp_size = OPT_TCP_SIZE;
07307 
07308 
07309 /********************************************************************\
07310 *                       conversion functions                         *
07311 \********************************************************************/
07312 
07313 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
07314 {
07315    *convert_flags = 0;
07316 
07317    /* big/little endian conversion */
07318    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
07319         (hw_type & DRI_LITTLE_ENDIAN)) ||
07320        ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
07321       *convert_flags |= CF_ENDIAN;
07322 
07323    /* float conversion between IEEE and VAX G */
07324    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
07325       *convert_flags |= CF_VAX2IEEE;
07326 
07327    /* float conversion between VAX G and IEEE */
07328    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
07329       *convert_flags |= CF_IEEE2VAX;
07330 
07331    /* ASCII format */
07332    if (remote_hw_type & DR_ASCII)
07333       *convert_flags |= CF_ASCII;
07334 }
07335 
07336 /********************************************************************/
07337 void rpc_get_convert_flags(INT * convert_flags)
07338 {
07339    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE), _server_connection.remote_hw_type, convert_flags);
07340 }
07341 
07342 /********************************************************************/
07343 void rpc_ieee2vax_float(float *var)
07344 {
07345    unsigned short int lo, hi;
07346 
07347    /* swap hi and lo word */
07348    lo = *((short int *) (var) + 1);
07349    hi = *((short int *) (var));
07350 
07351    /* correct exponent */
07352    if (lo != 0)
07353       lo += 0x100;
07354 
07355    *((short int *) (var) + 1) = hi;
07356    *((short int *) (var)) = lo;
07357 }
07358 
07359 void rpc_vax2ieee_float(float *var)
07360 {
07361    unsigned short int lo, hi;
07362 
07363    /* swap hi and lo word */
07364    lo = *((short int *) (var) + 1);
07365    hi = *((short int *) (var));
07366 
07367    /* correct exponent */
07368    if (hi != 0)
07369       hi -= 0x100;
07370 
07371    *((short int *) (var) + 1) = hi;
07372    *((short int *) (var)) = lo;
07373 
07374 }
07375 
07376 void rpc_vax2ieee_double(double *var)
07377 {
07378    unsigned short int i1, i2, i3, i4;
07379 
07380    /* swap words */
07381    i1 = *((short int *) (var) + 3);
07382    i2 = *((short int *) (var) + 2);
07383    i3 = *((short int *) (var) + 1);
07384    i4 = *((short int *) (var));
07385 
07386    /* correct exponent */
07387    if (i4 != 0)
07388       i4 -= 0x20;
07389 
07390    *((short int *) (var) + 3) = i4;
07391    *((short int *) (var) + 2) = i3;
07392    *((short int *) (var) + 1) = i2;
07393    *((short int *) (var)) = i1;
07394 }
07395 
07396 void rpc_ieee2vax_double(double *var)
07397 {
07398    unsigned short int i1, i2, i3, i4;
07399 
07400    /* swap words */
07401    i1 = *((short int *) (var) + 3);
07402    i2 = *((short int *) (var) + 2);
07403    i3 = *((short int *) (var) + 1);
07404    i4 = *((short int *) (var));
07405 
07406    /* correct exponent */
07407    if (i1 != 0)
07408       i1 += 0x20;
07409 
07410    *((short int *) (var) + 3) = i4;
07411    *((short int *) (var) + 2) = i3;
07412    *((short int *) (var) + 1) = i2;
07413    *((short int *) (var)) = i1;
07414 }
07415 
07416 /********************************************************************/
07417 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
07418 {
07419 
07420    if (convert_flags & CF_ENDIAN) {
07421       if (tid == TID_WORD || tid == TID_SHORT)
07422          WORD_SWAP(data);
07423       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
07424          DWORD_SWAP(data);
07425       if (tid == TID_DOUBLE)
07426          QWORD_SWAP(data);
07427    }
07428 
07429    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
07430        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
07431       if (tid == TID_FLOAT)
07432          rpc_ieee2vax_float((float *) data);
07433       if (tid == TID_DOUBLE)
07434          rpc_ieee2vax_double((double *) data);
07435    }
07436 
07437    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
07438        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
07439       if (tid == TID_FLOAT)
07440          rpc_vax2ieee_float((float *) data);
07441       if (tid == TID_DOUBLE)
07442          rpc_vax2ieee_double((double *) data);
07443    }
07444 }
07445 
07446 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
07447 /********************************************************************\
07448 
07449   Routine: rpc_convert_data
07450 
07451   Purpose: Convert data format between differenct computers
07452 
07453   Input:
07454     void   *data            Pointer to data
07455     INT    tid              Type ID of data, one of TID_xxx
07456     INT    flags            Combination of following flags:
07457                               RPC_IN: data is input parameter
07458                               RPC_OUT: data is output variable
07459                               RPC_FIXARRAY, RPC_VARARRAY: data is array
07460                                 of "size" bytes (see next param.)
07461                               RPC_OUTGOING: data is outgoing
07462     INT    total_size       Size of bytes of data. Used for variable
07463                             length arrays.
07464     INT    convert_flags    Flags for data conversion
07465 
07466   Output:
07467     void   *data            Is converted according to _convert_flag
07468                             value
07469 
07470   Function value:
07471     RPC_SUCCESS             Successful completion
07472 
07473 \********************************************************************/
07474 {
07475    INT i, n, single_size;
07476    char *p;
07477 
07478    /* convert array */
07479    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
07480       single_size = tid_size[tid];
07481       /* don't convert TID_ARRAY & TID_STRUCT */
07482       if (single_size == 0)
07483          return;
07484 
07485       n = total_size / single_size;
07486 
07487       for (i = 0; i < n; i++) {
07488          p = (char *) data + (i * single_size);
07489          rpc_convert_single(p, tid, flags, convert_flags);
07490       }
07491    } else {
07492       rpc_convert_single(data, tid, flags, convert_flags);
07493    }
07494 }
07495 
07496 /********************************************************************\
07497 *                       type ID functions                            *
07498 \********************************************************************/
07499 
07500 INT rpc_tid_size(INT id)
07501 {
07502    if (id < TID_LAST)
07503       return tid_size[id];
07504 
07505    return 0;
07506 }
07507 
07508 char *rpc_tid_name(INT id)
07509 {
07510    if (id < TID_LAST)
07511       return tid_name[id];
07512    else
07513       return "<unknown>";
07514 }
07515 
07516 
07517 /**dox***************************************************************/
07518 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07519 
07520 /********************************************************************\
07521 *                        client functions                            *
07522 \********************************************************************/
07523 
07524 /********************************************************************/
07525 /**
07526 Register RPC client for standalone mode (without standard
07527            midas server)
07528 @param list           Array of RPC_LIST structures containing
07529                             function IDs and parameter definitions.
07530                             The end of the list must be indicated by
07531                             a function ID of zero.
07532 @param name          Name of this client
07533 @return RPC_SUCCESS
07534 */
07535 INT rpc_register_client(char *name, RPC_LIST * list)
07536 {
07537    rpc_set_name(name);
07538    rpc_register_functions(rpc_get_internal_list(0), NULL);
07539    rpc_register_functions(list, NULL);
07540 
07541    return RPC_SUCCESS;
07542 }
07543 
07544 /********************************************************************/
07545 /**
07546 Register a set of RPC functions (both as clients or servers)
07547 @param new_list       Array of RPC_LIST structures containing
07548                             function IDs and parameter definitions.
07549                             The end of the list must be indicated by
07550                             a function ID of zero.
07551 @param func          Default dispatch function
07552 
07553 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
07554 */
07555 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
07556 {
07557    INT i, j, iold, inew;
07558 
07559    /* count number of new functions */
07560    for (i = 0; new_list[i].id != 0; i++) {
07561       /* check double defined functions */
07562       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
07563          if (rpc_list[j].id == new_list[i].id)
07564             return RPC_DOUBLE_DEFINED;
07565    }
07566    inew = i;
07567 
07568    /* count number of existing functions */
07569    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
07570    iold = i;
07571 
07572    /* allocate new memory for rpc_list */
07573    if (rpc_list == NULL)
07574       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
07575    else
07576       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
07577 
07578    if (rpc_list == NULL) {
07579       cm_msg(MERROR, "rpc_register_functions", "out of memory");
07580       return RPC_NO_MEMORY;
07581    }
07582 
07583    /* append new functions */
07584    for (i = iold; i < iold + inew; i++) {
07585       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
07586 
07587       /* set default dispatcher */
07588       if (rpc_list[i].dispatch == NULL)
07589          rpc_list[i].dispatch = func;
07590 
07591       /* check valid ID for user functions */
07592       if (new_list != rpc_get_internal_list(0) &&
07593           new_list != rpc_get_internal_list(1) &&
07594           (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
07595          cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID");
07596    }
07597 
07598    /* mark end of list */
07599    rpc_list[i].id = 0;
07600 
07601    return RPC_SUCCESS;
07602 }
07603 
07604 
07605 
07606 /**dox***************************************************************/
07607 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07608 
07609 /********************************************************************/
07610 INT rpc_deregister_functions()
07611 /********************************************************************\
07612 
07613   Routine: rpc_deregister_functions
07614 
07615   Purpose: Free memory of previously registered functions
07616 
07617   Input:
07618     none
07619 
07620   Output:
07621     none
07622 
07623   Function value:
07624     RPC_SUCCESS              Successful completion
07625 
07626 \********************************************************************/
07627 {
07628    if (rpc_list)
07629       M_FREE(rpc_list);
07630    rpc_list = NULL;
07631 
07632    return RPC_SUCCESS;
07633 }
07634 
07635 
07636 /********************************************************************/
07637 INT rpc_register_function(INT id, INT(*func) (INT, void **))
07638 /********************************************************************\
07639 
07640   Routine: rpc_register_function
07641 
07642   Purpose: Replace a dispatch function for a specific rpc routine
07643 
07644   Input:
07645     INT      id             RPC ID
07646     INT      *func          New dispatch function
07647 
07648   Output:
07649    <implicit: func gets copied to rpc_list>
07650 
07651   Function value:
07652    RPC_SUCCESS              Successful completion
07653    RPC_INVALID_ID           RPC ID not found
07654 
07655 \********************************************************************/
07656 {
07657    INT i;
07658 
07659    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
07660       if (rpc_list[i].id == id)
07661          break;
07662 
07663    if (rpc_list[i].id == id)
07664       rpc_list[i].dispatch = func;
07665    else
07666       return RPC_INVALID_ID;
07667 
07668    return RPC_SUCCESS;
07669 }
07670 
07671 
07672 /********************************************************************/
07673 INT rpc_client_dispatch(int sock)
07674 /********************************************************************\
07675 
07676   Routine: rpc_client_dispatch
07677 
07678   Purpose: Gets called whenever a client receives data from the
07679            server. Get set via rpc_connect. Internal use only.
07680 
07681 \********************************************************************/
07682 {
07683    INT hDB, hKey, n;
07684    NET_COMMAND *nc;
07685    INT status = 0;
07686    char net_buffer[256];
07687 
07688    nc = (NET_COMMAND *) net_buffer;
07689 
07690    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07691    if (n <= 0)
07692       return SS_ABORT;
07693 
07694    if (nc->header.routine_id == MSG_ODB) {
07695       /* update a changed record */
07696       hDB = *((INT *) nc->param);
07697       hKey = *((INT *) nc->param + 1);
07698       status = db_update_record(hDB, hKey, 0);
07699    }
07700 
07701    else if (nc->header.routine_id == MSG_WATCHDOG) {
07702       nc->header.routine_id = 1;
07703       nc->header.param_size = 0;
07704       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07705       status = RPC_SUCCESS;
07706    }
07707 
07708    else if (nc->header.routine_id == MSG_BM) {
07709       fd_set readfds;
07710       struct timeval timeout;
07711 
07712       /* receive further messages to empty TCP queue */
07713       do {
07714          FD_ZERO(&readfds);
07715          FD_SET(sock, &readfds);
07716 
07717          timeout.tv_sec = 0;
07718          timeout.tv_usec = 0;
07719 
07720          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
07721 
07722          if (FD_ISSET(sock, &readfds)) {
07723             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07724             if (n <= 0)
07725                return SS_ABORT;
07726 
07727             if (nc->header.routine_id == MSG_ODB) {
07728                /* update a changed record */
07729                hDB = *((INT *) nc->param);
07730                hKey = *((INT *) nc->param + 1);
07731                status = db_update_record(hDB, hKey, 0);
07732             }
07733 
07734             else if (nc->header.routine_id == MSG_WATCHDOG) {
07735                nc->header.routine_id = 1;
07736                nc->header.param_size = 0;
07737                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07738                status = RPC_SUCCESS;
07739             }
07740          }
07741 
07742       } while (FD_ISSET(sock, &readfds));
07743 
07744       /* poll event from server */
07745       status = bm_poll_event(FALSE);
07746    }
07747 
07748    return status;
07749 }
07750 
07751 
07752 /********************************************************************/
07753 INT rpc_client_connect(char *host_name, INT port, char *client_name, HNDLE * hConnection)
07754 /********************************************************************\
07755 
07756   Routine: rpc_client_connect
07757 
07758   Purpose: Establish a network connection to a remote client
07759 
07760   Input:
07761     char *host_name          IP address of host to connect to.
07762     INT  port                TPC port to connect to.
07763     char *clinet_name        Client program name
07764 
07765   Output:
07766     HNDLE *hConnection       Handle for new connection which can be used
07767                              in future rpc_call(hConnection....) calls
07768 
07769   Function value:
07770     RPC_SUCCESS              Successful completion
07771     RPC_NET_ERROR            Error in socket call
07772     RPC_NO_CONNECTION        Maximum number of connections reached
07773     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
07774 
07775 \********************************************************************/
07776 {
07777    INT i, status, index;
07778    struct sockaddr_in bind_addr;
07779    INT sock;
07780    INT remote_hw_type, hw_type;
07781    char str[200];
07782    char version[32], v1[32];
07783    char local_prog_name[NAME_LENGTH];
07784    char local_host_name[HOST_NAME_LENGTH];
07785    struct hostent *phe;
07786 
07787 #ifdef OS_WINNT
07788    {
07789       WSADATA WSAData;
07790 
07791       /* Start windows sockets */
07792       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
07793          return RPC_NET_ERROR;
07794    }
07795 #endif
07796 
07797    /* check if cm_connect_experiment was called */
07798    if (_client_name[0] == 0) {
07799       cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
07800       return RPC_NOT_REGISTERED;
07801    }
07802 
07803    /* check for broken connections */
07804    rpc_client_check();
07805 
07806    /* check if connection already exists */
07807    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07808       if (_client_connection[i].send_sock != 0 &&
07809           strcmp(_client_connection[i].host_name, host_name) == 0 && _client_connection[i].port == port) {
07810          *hConnection = i + 1;
07811          return RPC_SUCCESS;
07812       }
07813 
07814    /* search for free entry */
07815    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07816       if (_client_connection[i].send_sock == 0)
07817          break;
07818 
07819    /* open new network connection */
07820    if (i == MAX_RPC_CONNECTION) {
07821       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
07822       return RPC_NO_CONNECTION;
07823    }
07824 
07825    /* create a new socket for connecting to remote server */
07826    sock = socket(AF_INET, SOCK_STREAM, 0);
07827    if (sock == -1) {
07828       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
07829       return RPC_NET_ERROR;
07830    }
07831 
07832    index = i;
07833    strcpy(_client_connection[index].host_name, host_name);
07834    strcpy(_client_connection[index].client_name, client_name);
07835    _client_connection[index].port = port;
07836    _client_connection[index].exp_name[0] = 0;
07837    _client_connection[index].transport = RPC_TCP;
07838    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
07839    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
07840 
07841    /* connect to remote node */
07842    memset(&bind_addr, 0, sizeof(bind_addr));
07843    bind_addr.sin_family = AF_INET;
07844    bind_addr.sin_addr.s_addr = 0;
07845    bind_addr.sin_port = htons((short) port);
07846 
07847 #ifdef OS_VXWORKS
07848    {
07849       INT host_addr;
07850 
07851       host_addr = hostGetByName(host_name);
07852       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
07853    }
07854 #else
07855    phe = gethostbyname(host_name);
07856    if (phe == NULL) {
07857       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
07858       return RPC_NET_ERROR;
07859    }
07860    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
07861 #endif
07862 
07863 #ifdef OS_UNIX
07864    do {
07865       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
07866 
07867       /* don't return if an alarm signal was cought */
07868    } while (status == -1 && errno == EINTR);
07869 #else
07870    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
07871 #endif
07872 
07873    if (status != 0) {
07874       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
07875          message should be displayed by application */
07876       return RPC_NET_ERROR;
07877    }
07878 
07879    /* set TCP_NODELAY option for better performance */
07880    i = 1;
07881    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
07882 
07883    /* send local computer info */
07884    rpc_get_name(local_prog_name);
07885    gethostname(local_host_name, sizeof(local_host_name));
07886 
07887    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
07888    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name, local_host_name);
07889 
07890    send(sock, str, strlen(str) + 1, 0);
07891 
07892    /* receive remote computer info */
07893    i = recv_string(sock, str, sizeof(str), 10000);
07894    if (i <= 0) {
07895       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s", str);
07896       return RPC_NET_ERROR;
07897    }
07898 
07899    remote_hw_type = version[0] = 0;
07900    sscanf(str, "%d %s", &remote_hw_type, version);
07901    _client_connection[index].remote_hw_type = remote_hw_type;
07902    _client_connection[index].send_sock = sock;
07903 
07904    /* print warning if version patch level doesn't agree */
07905    strcpy(v1, version);
07906    if (strchr(v1, '.'))
07907       if (strchr(strchr(v1, '.') + 1, '.'))
07908          *strchr(strchr(v1, '.') + 1, '.') = 0;
07909 
07910    strcpy(str, cm_get_version());
07911    if (strchr(str, '.'))
07912       if (strchr(strchr(str, '.') + 1, '.'))
07913          *strchr(strchr(str, '.') + 1, '.') = 0;
07914 
07915    if (strcmp(v1, str) != 0) {
07916       sprintf(str, "remote MIDAS version %s differs from local version %s", version, cm_get_version());
07917       cm_msg(MERROR, "rpc_client_connect", str);
07918    }
07919 
07920    *hConnection = index + 1;
07921 
07922    return RPC_SUCCESS;
07923 }
07924 
07925 /********************************************************************/
07926 void rpc_client_check()
07927 /********************************************************************\
07928 
07929   Routine: rpc_client_check
07930 
07931   Purpose: Check all client connections if remote client closed link
07932 
07933   Function value:
07934     RPC_SUCCESS              Successful completion
07935     RPC_NET_ERROR            Error in socket call
07936     RPC_NO_CONNECTION        Maximum number of connections reached
07937     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
07938 
07939 \********************************************************************/
07940 {
07941    INT i, status;
07942 
07943    /* check for broken connections */
07944    for (i = 0; i < MAX_RPC_CONNECTION; i++)
07945       if (_client_connection[i].send_sock != 0) {
07946          int sock;
07947          fd_set readfds;
07948          struct timeval timeout;
07949          char buffer[64];
07950 
07951          sock = _client_connection[i].send_sock;
07952          FD_ZERO(&readfds);
07953          FD_SET(sock, &readfds);
07954 
07955          timeout.tv_sec = 0;
07956          timeout.tv_usec = 0;
07957 
07958          do {
07959             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
07960          } while (status == -1);        /* dont return if an alarm signal was cought */
07961 
07962          if (FD_ISSET(sock, &readfds)) {
07963             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
07964 
07965             if (equal_ustring(buffer, "EXIT")) {
07966                /* normal exit */
07967                closesocket(sock);
07968                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
07969             }
07970 
07971             if (status <= 0) {
07972                cm_msg(MERROR, "rpc_client_check",
07973                       "Connection broken to \"%s\" on host %s",
07974                       _client_connection[i].client_name, _client_connection[i].host_name);
07975 
07976                /* connection broken -> reset */
07977                closesocket(sock);
07978                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
07979             }
07980          }
07981       }
07982 }
07983 
07984 
07985 /********************************************************************/
07986 INT rpc_server_connect(char *host_name, char *exp_name)
07987 /********************************************************************\
07988 
07989   Routine: rpc_server_connect
07990 
07991   Purpose: Extablish a network connection to a remote MIDAS
07992            server using a callback scheme.
07993 
07994   Input:
07995     char *host_name         IP address of host to connect to.
07996 
07997     INT  port               TPC port to connect to.
07998 
07999     char *exp_name          Name of experiment to connect to. By using
08000                             this name, several experiments (e.g. online
08001                             DAQ and offline analysis) can run simultan-
08002                             eously on the same host.
08003 
08004   Output:
08005     none
08006 
08007   Function value:
08008     RPC_SUCCESS              Successful completion
08009     RPC_NET_ERROR            Error in socket call
08010     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08011     CM_UNDEF_EXP             Undefined experiment on server
08012 
08013 \********************************************************************/
08014 {
08015    INT i, status, flag;
08016    struct sockaddr_in bind_addr;
08017    INT sock, lsock1, lsock2, lsock3;
08018    INT listen_port1, listen_port2, listen_port3;
08019    INT remote_hw_type, hw_type;
08020    unsigned int size;
08021    char str[200], version[32], v1[32];
08022    char local_prog_name[NAME_LENGTH];
08023    struct hostent *phe;
08024    fd_set readfds;
08025    struct timeval timeout;
08026 
08027 #ifdef OS_WINNT
08028    {
08029       WSADATA WSAData;
08030 
08031       /* Start windows sockets */
08032       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08033          return RPC_NET_ERROR;
08034    }
08035 #endif
08036 
08037    /* check if local connection */
08038    if (host_name[0] == 0)
08039       return RPC_SUCCESS;
08040 
08041    /* register system functions */
08042    rpc_register_functions(rpc_get_internal_list(0), NULL);
08043 
08044    /* check if cm_connect_experiment was called */
08045    if (_client_name[0] == 0) {
08046       cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
08047       return RPC_NOT_REGISTERED;
08048    }
08049 
08050    /* check if connection already exists */
08051    if (_server_connection.send_sock != 0)
08052       return RPC_SUCCESS;
08053 
08054    strcpy(_server_connection.host_name, host_name);
08055    strcpy(_server_connection.exp_name, exp_name);
08056    _server_connection.transport = RPC_TCP;
08057    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08058 
08059    /* create new TCP sockets for listening */
08060    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08061    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08062    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08063    if (lsock3 == -1) {
08064       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08065       return RPC_NET_ERROR;
08066    }
08067 
08068    flag = 1;
08069    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08070    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08071    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08072 
08073    /* let OS choose any port number */
08074    memset(&bind_addr, 0, sizeof(bind_addr));
08075    bind_addr.sin_family = AF_INET;
08076    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08077    bind_addr.sin_port = 0;
08078 
08079    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08080    bind_addr.sin_port = 0;
08081    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08082    bind_addr.sin_port = 0;
08083    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08084    if (status < 0) {
08085       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08086       return RPC_NET_ERROR;
08087    }
08088 
08089    /* listen for connection */
08090    status = listen(lsock1, 1);
08091    status = listen(lsock2, 1);
08092    status = listen(lsock3, 1);
08093    if (status < 0) {
08094       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08095       return RPC_NET_ERROR;
08096    }
08097 
08098    /* find out which port OS has chosen */
08099    size = sizeof(bind_addr);
08100    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08101    listen_port1 = ntohs(bind_addr.sin_port);
08102    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08103    listen_port2 = ntohs(bind_addr.sin_port);
08104    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08105    listen_port3 = ntohs(bind_addr.sin_port);
08106 
08107    /* create a new socket for connecting to remote server */
08108    sock = socket(AF_INET, SOCK_STREAM, 0);
08109    if (sock == -1) {
08110       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08111       return RPC_NET_ERROR;
08112    }
08113 
08114    /* connect to remote node */
08115    memset(&bind_addr, 0, sizeof(bind_addr));
08116    bind_addr.sin_family = AF_INET;
08117    bind_addr.sin_addr.s_addr = 0;
08118    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08119 
08120 #ifdef OS_VXWORKS
08121    {
08122       INT host_addr;
08123 
08124       host_addr = hostGetByName(host_name);
08125       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08126    }
08127 #else
08128    phe = gethostbyname(host_name);
08129    if (phe == NULL) {
08130       cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08131       return RPC_NET_ERROR;
08132    }
08133    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08134 #endif
08135 
08136 #ifdef OS_UNIX
08137    do {
08138       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08139 
08140       /* don't return if an alarm signal was cought */
08141    } while (status == -1 && errno == EINTR);
08142 #else
08143    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08144 #endif
08145 
08146    if (status != 0) {
08147 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08148       return RPC_NET_ERROR;
08149    }
08150 
08151    /* connect to experiment */
08152    if (exp_name[0] == 0)
08153       sprintf(str, "C %d %d %d %s Default", listen_port1, listen_port2, listen_port3, cm_get_version());
08154    else
08155       sprintf(str, "C %d %d %d %s %s", listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08156 
08157    send(sock, str, strlen(str) + 1, 0);
08158    i = recv_string(sock, str, sizeof(str), 10000);
08159    closesocket(sock);
08160    if (i <= 0) {
08161       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08162       return RPC_NET_ERROR;
08163    }
08164 
08165    status = version[0] = 0;
08166    sscanf(str, "%d %s", &status, version);
08167 
08168    if (status == 2) {
08169 /*  message "undefined experiment" should be displayed by application */
08170       return CM_UNDEF_EXP;
08171    }
08172 
08173    /* print warning if version patch level doesn't agree */
08174    strcpy(v1, version);
08175    if (strchr(v1, '.'))
08176       if (strchr(strchr(v1, '.') + 1, '.'))
08177          *strchr(strchr(v1, '.') + 1, '.') = 0;
08178 
08179    strcpy(str, cm_get_version());
08180    if (strchr(str, '.'))
08181       if (strchr(strchr(str, '.') + 1, '.'))
08182          *strchr(strchr(str, '.') + 1, '.') = 0;
08183 
08184    if (strcmp(v1, str) != 0) {
08185       sprintf(str, "remote MIDAS version %s differs from local version %s", version, cm_get_version());
08186       cm_msg(MERROR, "rpc_server_connect", str);
08187    }
08188 
08189    /* wait for callback on send and recv socket with timeout */
08190    FD_ZERO(&readfds);
08191    FD_SET(lsock1, &readfds);
08192    FD_SET(lsock2, &readfds);
08193    FD_SET(lsock3, &readfds);
08194 
08195    timeout.tv_sec = 5;
08196    timeout.tv_usec = 0;
08197 
08198    do {
08199       status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08200 
08201       /* if an alarm signal was cought, restart select with reduced timeout */
08202       if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
08203          timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
08204 
08205    } while (status == -1);      /* dont return if an alarm signal was cought */
08206 
08207    if (!FD_ISSET(lsock1, &readfds)) {
08208       cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
08209       closesocket(lsock1);
08210       closesocket(lsock2);
08211       closesocket(lsock3);
08212       return RPC_NET_ERROR;
08213    }
08214 
08215    size = sizeof(bind_addr);
08216 
08217    _server_connection.send_sock = accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08218 
08219    _server_connection.recv_sock = accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08220 
08221    _server_connection.event_sock = accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08222 
08223    if (_server_connection.send_sock == -1 ||
08224        _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
08225       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08226       return RPC_NET_ERROR;
08227    }
08228 
08229    closesocket(lsock1);
08230    closesocket(lsock2);
08231    closesocket(lsock3);
08232 
08233    /* set TCP_NODELAY option for better performance */
08234    flag = 1;
08235    setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08236    setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08237 
08238    /* increase send buffer size to 64kB */
08239    flag = 0x10000;
08240    setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
08241 
08242    /* send local computer info */
08243    rpc_get_name(local_prog_name);
08244    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08245    sprintf(str, "%d %s", hw_type, local_prog_name);
08246 
08247    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08248 
08249    /* receive remote computer info */
08250    i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
08251    if (i <= 0) {
08252       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
08253       return RPC_NET_ERROR;
08254    }
08255 
08256    sscanf(str, "%d", &remote_hw_type);
08257    _server_connection.remote_hw_type = remote_hw_type;
08258 
08259    /* set dispatcher which receives database updates */
08260    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection, (int (*)(void)) rpc_client_dispatch);
08261 
08262    return RPC_SUCCESS;
08263 }
08264 
08265 /********************************************************************/
08266 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08267 /********************************************************************\
08268 
08269   Routine: rpc_client_disconnect
08270 
08271   Purpose: Close a rpc connection to a MIDAS client
08272 
08273   Input:
08274     HNDLE  hConn           Handle of connection
08275     BOOL   bShutdown       Shut down remote server if TRUE
08276 
08277   Output:
08278     none
08279 
08280   Function value:
08281    RPC_SUCCESS             Successful completion
08282 
08283 \********************************************************************/
08284 {
08285    INT i;
08286 
08287    if (hConn == -1) {
08288       /* close all open connections */
08289       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
08290          if (_client_connection[i].send_sock != 0)
08291             rpc_client_disconnect(i + 1, FALSE);
08292 
08293       /* close server connection from other clients */
08294       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08295          if (_server_acception[i].recv_sock) {
08296             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
08297             closesocket(_server_acception[i].recv_sock);
08298          }
08299    } else {
08300       /* notify server about exit */
08301 
08302       /* set FTCP mode (helps for rebooted VxWorks nodes) */
08303       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
08304       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
08305 
08306       /* close socket */
08307       if (_client_connection[hConn - 1].send_sock)
08308          closesocket(_client_connection[hConn - 1].send_sock);
08309 
08310       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
08311    }
08312 
08313    return RPC_SUCCESS;
08314 }
08315 
08316 
08317 /********************************************************************/
08318 INT rpc_server_disconnect()
08319 /********************************************************************\
08320 
08321   Routine: rpc_server_disconnect
08322 
08323   Purpose: Close a rpc connection to a MIDAS server and close all
08324            server connections from other clients
08325 
08326   Input:
08327     none
08328 
08329   Output:
08330     none
08331 
08332   Function value:
08333    RPC_SUCCESS             Successful completion
08334    RPC_NET_ERROR           Error in socket call
08335    RPC_NO_CONNECTION       Maximum number of connections reached
08336 
08337 \********************************************************************/
08338 {
08339    static int rpc_server_disconnect_recursion_level = 0;
08340 
08341    if (rpc_server_disconnect_recursion_level)
08342       return RPC_SUCCESS;
08343 
08344    rpc_server_disconnect_recursion_level = 1;
08345 
08346    /* flush remaining events */
08347    rpc_flush_event();
08348 
08349    /* notify server about exit */
08350    rpc_call(RPC_ID_EXIT);
08351 
08352    /* close sockets */
08353    closesocket(_server_connection.send_sock);
08354    closesocket(_server_connection.recv_sock);
08355    closesocket(_server_connection.event_sock);
08356 
08357    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
08358 
08359    rpc_server_disconnect_recursion_level = 0;
08360    return RPC_SUCCESS;
08361 }
08362 
08363 
08364 /********************************************************************/
08365 INT rpc_is_remote(void)
08366 /********************************************************************\
08367 
08368   Routine: rpc_is_remote
08369 
08370   Purpose: Return true if program is connected to a remote server
08371 
08372   Input:
08373    none
08374 
08375   Output:
08376     none
08377 
08378   Function value:
08379     INT    RPC connection index
08380 
08381 \********************************************************************/
08382 {
08383    return _server_connection.send_sock != 0;
08384 }
08385 
08386 
08387 /********************************************************************/
08388 INT rpc_get_server_acception(void)
08389 /********************************************************************\
08390 
08391   Routine: rpc_get_server_acception
08392 
08393   Purpose: Return actual RPC server connection index
08394 
08395   Input:
08396    none
08397 
08398   Output:
08399     none
08400 
08401   Function value:
08402     INT    RPC server connection index
08403 
08404 \********************************************************************/
08405 {
08406    return _server_acception_index;
08407 }
08408 
08409 
08410 /********************************************************************/
08411 INT rpc_set_server_acception(INT index)
08412 /********************************************************************\
08413 
08414   Routine: rpc_set_server_acception
08415 
08416   Purpose: Set actual RPC server connection index
08417 
08418   Input:
08419     INT  index              Server index
08420 
08421   Output:
08422     none
08423 
08424   Function value:
08425     RPC_SUCCESS             Successful completion
08426 
08427 \********************************************************************/
08428 {
08429    _server_acception_index = index;
08430    return RPC_SUCCESS;
08431 }
08432 
08433 
08434 /********************************************************************/
08435 INT rpc_get_option(HNDLE hConn, INT item)
08436 /********************************************************************\
08437 
08438   Routine: rpc_get_option
08439 
08440   Purpose: Get actual RPC option
08441 
08442   Input:
08443     HNDLE hConn             RPC connection handle
08444     INT   item              One of RPC_Oxxx
08445 
08446   Output:
08447     none
08448 
08449   Function value:
08450     INT                     Actual option
08451 
08452 \********************************************************************/
08453 {
08454    switch (item) {
08455    case RPC_OTIMEOUT:
08456       if (hConn == -1)
08457          return _server_connection.rpc_timeout;
08458       return _client_connection[hConn - 1].rpc_timeout;
08459 
08460    case RPC_OTRANSPORT:
08461       if (hConn == -1)
08462          return _server_connection.transport;
08463       return _client_connection[hConn - 1].transport;
08464 
08465    case RPC_OHW_TYPE:
08466       {
08467          INT tmp_type, size;
08468          DWORD dummy;
08469          unsigned char *p;
08470          float f;
08471          double d;
08472 
08473          tmp_type = 0;
08474 
08475          /* test pointer size */
08476          size = sizeof(p);
08477          if (size == 2)
08478             tmp_type |= DRI_16;
08479          if (size == 4)
08480             tmp_type |= DRI_32;
08481          if (size == 8)
08482             tmp_type |= DRI_64;
08483 
08484          /* test if little or big endian machine */
08485          dummy = 0x12345678;
08486          p = (unsigned char *) &dummy;
08487          if (*p == 0x78)
08488             tmp_type |= DRI_LITTLE_ENDIAN;
08489          else if (*p == 0x12)
08490             tmp_type |= DRI_BIG_ENDIAN;
08491          else
08492             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
08493 
08494          /* floating point format */
08495          f = (float) 1.2345;
08496          dummy = 0;
08497          memcpy(&dummy, &f, sizeof(f));
08498          if ((dummy & 0xFF) == 0x19 &&
08499              ((dummy >> 8) & 0xFF) == 0x04 &&
08500              ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
08501             tmp_type |= DRF_IEEE;
08502          else if ((dummy & 0xFF) == 0x9E &&
08503                   ((dummy >> 8) & 0xFF) == 0x40 &&
08504                   ((dummy >> 16) & 0xFF) == 0x19 && ((dummy >> 24) & 0xFF) == 0x04)
08505             tmp_type |= DRF_G_FLOAT;
08506          else
08507             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08508 
08509          d = (double) 1.2345;
08510          dummy = 0;
08511          memcpy(&dummy, &d, sizeof(f));
08512          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
08513              ((dummy >> 8) & 0xFF) == 0x97 &&
08514              ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
08515             tmp_type |= DRF_IEEE;
08516          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
08517                   ((dummy >> 8) & 0xFF) == 0xC0 &&
08518                   ((dummy >> 16) & 0xFF) == 0xF3 && ((dummy >> 24) & 0xFF) == 0x3F)
08519             tmp_type |= DRF_IEEE;
08520          else if ((dummy & 0xFF) == 0x13 &&
08521                   ((dummy >> 8) & 0xFF) == 0x40 &&
08522                   ((dummy >> 16) & 0xFF) == 0x83 && ((dummy >> 24) & 0xFF) == 0xC0)
08523             tmp_type |= DRF_G_FLOAT;
08524          else if ((dummy & 0xFF) == 0x9E &&
08525                   ((dummy >> 8) & 0xFF) == 0x40 &&
08526                   ((dummy >> 16) & 0xFF) == 0x18 && ((dummy >> 24) & 0xFF) == 0x04)
08527             cm_msg(MERROR, "rpc_get_option",
08528                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
08529          else
08530             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08531 
08532          return tmp_type;
08533       }
08534 
08535    default:
08536       cm_msg(MERROR, "rpc_get_option", "invalid argument");
08537       break;
08538    }
08539 
08540    return 0;
08541 }
08542 
08543 /**dox***************************************************************/
08544 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08545 
08546 /********************************************************************/
08547 /**
08548 Set RPC option
08549 @param hConn              RPC connection handle
08550 @param item               One of RPC_Oxxx
08551 @param value              Value to set
08552 @return RPC_SUCCESS
08553 */
08554 INT rpc_set_option(HNDLE hConn, INT item, INT value)
08555 {
08556    switch (item) {
08557    case RPC_OTIMEOUT:
08558       if (hConn == -1)
08559          _server_connection.rpc_timeout = value;
08560       else
08561          _client_connection[hConn - 1].rpc_timeout = value;
08562       break;
08563 
08564    case RPC_OTRANSPORT:
08565       if (hConn == -1)
08566          _server_connection.transport = value;
08567       else
08568          _client_connection[hConn - 1].transport = value;
08569       break;
08570 
08571    case RPC_NODELAY:
08572       if (hConn == -1)
08573          setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
08574       else
08575          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP,
08576                     TCP_NODELAY, (char *) &value, sizeof(value));
08577       break;
08578 
08579    default:
08580       cm_msg(MERROR, "rpc_set_option", "invalid argument");
08581       break;
08582    }
08583 
08584    return 0;
08585 }
08586 
08587 
08588 /**dox***************************************************************/
08589 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08590 
08591 /********************************************************************/
08592 POINTER_T rpc_get_server_option(INT item)
08593 /********************************************************************\
08594 
08595   Routine: rpc_get_server_option
08596 
08597   Purpose: Get actual RPC option for server connection
08598 
08599   Input:
08600     INT  item               One of RPC_Oxxx
08601 
08602   Output:
08603     none
08604 
08605   Function value:
08606     INT                     Actual option
08607 
08608 \********************************************************************/
08609 {
08610    INT i;
08611 
08612    if (item == RPC_OSERVER_TYPE)
08613       return _server_type;
08614 
08615    if (item == RPC_OSERVER_NAME)
08616       return (POINTER_T) _server_name;
08617 
08618    /* return 0 for local calls */
08619    if (_server_type == ST_NONE)
08620       return 0;
08621 
08622    /* check which connections belongs to caller */
08623    if (_server_type == ST_MTHREAD) {
08624       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08625          if (_server_acception[i].tid == ss_gettid())
08626             break;
08627    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08628       i = MAX(0, _server_acception_index - 1);
08629    else
08630       i = 0;
08631 
08632    switch (item) {
08633    case RPC_CONVERT_FLAGS:
08634       return _server_acception[i].convert_flags;
08635    case RPC_ODB_HANDLE:
08636       return _server_acception[i].odb_handle;
08637    case RPC_CLIENT_HANDLE:
08638       return _server_acception[i].client_handle;
08639    case RPC_SEND_SOCK:
08640       return _server_acception[i].send_sock;
08641    case RPC_WATCHDOG_TIMEOUT:
08642       return _server_acception[i].watchdog_timeout;
08643    }
08644 
08645    return 0;
08646 }
08647 
08648 
08649 /********************************************************************/
08650 INT rpc_set_server_option(INT item, POINTER_T value)
08651 /********************************************************************\
08652 
08653   Routine: rpc_set_server_option
08654 
08655   Purpose: Set RPC option for server connection
08656 
08657   Input:
08658    INT  item               One of RPC_Oxxx
08659    INT  value              Value to set
08660 
08661   Output:
08662     none
08663 
08664   Function value:
08665     RPC_SUCCESS             Successful completion
08666 
08667 \********************************************************************/
08668 {
08669    INT i;
08670 
08671    if (item == RPC_OSERVER_TYPE) {
08672       _server_type = value;
08673       return RPC_SUCCESS;
08674    }
08675    if (item == RPC_OSERVER_NAME) {
08676       strcpy(_server_name, (char *) value);
08677       return RPC_SUCCESS;
08678    }
08679 
08680    /* check which connections belongs to caller */
08681    if (_server_type == ST_MTHREAD) {
08682       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08683          if (_server_acception[i].tid == ss_gettid())
08684             break;
08685    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08686       i = MAX(0, _server_acception_index - 1);
08687    else
08688       i = 0;
08689 
08690    switch (item) {
08691    case RPC_CONVERT_FLAGS:
08692       _server_acception[i].convert_flags = value;
08693       break;
08694    case RPC_ODB_HANDLE:
08695       _server_acception[i].odb_handle = value;
08696       break;
08697    case RPC_CLIENT_HANDLE:
08698       _server_acception[i].client_handle = value;
08699       break;
08700    case RPC_WATCHDOG_TIMEOUT:
08701       _server_acception[i].watchdog_timeout = value;
08702       break;
08703    }
08704 
08705    return RPC_SUCCESS;
08706 }
08707 
08708 
08709 /********************************************************************/
08710 INT rpc_get_name(char *name)
08711 /********************************************************************\
08712 
08713   Routine: rpc_get_name
08714 
08715   Purpose: Get name set by rpc_set_name
08716 
08717   Input:
08718     none
08719 
08720   Output:
08721     char*  name             The location pointed by *name receives a
08722                             copy of the _prog_name
08723 
08724   Function value:
08725     RPC_SUCCESS             Successful completion
08726 
08727 \********************************************************************/
08728 {
08729    strcpy(name, _client_name);
08730 
08731    return RPC_SUCCESS;
08732 }
08733 
08734 
08735 /********************************************************************/
08736 INT rpc_set_name(char *name)
08737 /********************************************************************\
08738 
08739   Routine: rpc_set_name
08740 
08741   Purpose: Set name of actual program for further rpc connections
08742 
08743   Input:
08744    char *name               Program name, up to NAME_LENGTH chars,
08745                             no blanks
08746 
08747   Output:
08748     none
08749 
08750   Function value:
08751     RPC_SUCCESS             Successful completion
08752 
08753 \********************************************************************/
08754 {
08755    strcpy(_client_name, name);
08756 
08757    return RPC_SUCCESS;
08758 }
08759 
08760 
08761 /********************************************************************/
08762 INT rpc_set_debug(void (*func) (char *), INT mode)
08763 /********************************************************************\
08764 
08765   Routine: rpc_set_debug
08766 
08767   Purpose: Set a function which is called on every RPC call to
08768            display the function name and parameters of the RPC
08769            call.
08770 
08771   Input:
08772    void *func(char*)        Pointer to function.
08773    INT  mode                Debug mode
08774 
08775   Output:
08776     none
08777 
08778   Function value:
08779     RPC_SUCCESS             Successful completion
08780 
08781 \********************************************************************/
08782 {
08783    _debug_print = func;
08784    _debug_mode = mode;
08785    return RPC_SUCCESS;
08786 }
08787 
08788 /********************************************************************/
08789 void rpc_debug_printf(char *format, ...)
08790 /********************************************************************\
08791 
08792   Routine: rpc_debug_print
08793 
08794   Purpose: Calls function set via rpc_set_debug to output a string.
08795 
08796   Input:
08797    char *str                Debug string
08798 
08799   Output:
08800     none
08801 
08802 \********************************************************************/
08803 {
08804    va_list argptr;
08805    char str[1000];
08806 
08807    if (_debug_mode) {
08808       va_start(argptr, format);
08809       vsprintf(str, (char *) format, argptr);
08810       va_end(argptr);
08811 
08812       if (_debug_print) {
08813          strcat(str, "\n");
08814          _debug_print(str);
08815       } else
08816          puts(str);
08817    }
08818 }
08819 
08820 /********************************************************************/
08821 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
08822 {
08823    switch (arg_type) {
08824       /* On the stack, the minimum parameter size is sizeof(int).
08825          To avoid problems on little endian systems, treat all
08826          smaller parameters as int's */
08827    case TID_BYTE:
08828    case TID_SBYTE:
08829    case TID_CHAR:
08830    case TID_WORD:
08831    case TID_SHORT:
08832       *((int *) arg) = va_arg(*arg_ptr, int);
08833       break;
08834 
08835    case TID_INT:
08836    case TID_BOOL:
08837       *((INT *) arg) = va_arg(*arg_ptr, INT);
08838       break;
08839 
08840    case TID_DWORD:
08841       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
08842       break;
08843 
08844       /* float variables are passed as double by the compiler */
08845    case TID_FLOAT:
08846       *((float *) arg) = (float) va_arg(*arg_ptr, double);
08847       break;
08848 
08849    case TID_DOUBLE:
08850       *((double *) arg) = va_arg(*arg_ptr, double);
08851       break;
08852 
08853    case TID_ARRAY:
08854       *((char **) arg) = va_arg(*arg_ptr, char *);
08855       break;
08856    }
08857 }
08858 
08859 
08860 /********************************************************************/
08861 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
08862 /********************************************************************\
08863 
08864   Routine: rpc_client_call
08865 
08866   Purpose: Call a function on a MIDAS client
08867 
08868   Input:
08869     INT  hConn              Client connection
08870     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
08871 
08872     ...                     variable argument list
08873 
08874   Output:
08875     (depends on argument list)
08876 
08877   Function value:
08878     RPC_SUCCESS             Successful completion
08879     RPC_NET_ERROR           Error in socket call
08880     RPC_NO_CONNECTION       No active connection
08881     RPC_TIMEOUT             Timeout in RPC call
08882     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
08883     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
08884 
08885 \********************************************************************/
08886 {
08887    va_list ap, aptmp;
08888    char arg[8], arg_tmp[8];
08889    INT arg_type, transport, rpc_timeout;
08890    INT i, index, status, rpc_index;
08891    INT param_size, arg_size, send_size;
08892    INT tid, flags;
08893    fd_set readfds;
08894    struct timeval timeout;
08895    char *param_ptr, str[80];
08896    BOOL bpointer, bbig;
08897    NET_COMMAND *nc;
08898    int send_sock;
08899 
08900    index = hConn - 1;
08901 
08902    if (_client_connection[index].send_sock == 0) {
08903       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
08904       return RPC_NO_CONNECTION;
08905    }
08906 
08907    send_sock = _client_connection[index].send_sock;
08908    rpc_timeout = _client_connection[index].rpc_timeout;
08909    transport = _client_connection[index].transport;
08910 
08911    /* init network buffer */
08912    if (_net_send_buffer_size == 0) {
08913       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
08914       if (_net_send_buffer == NULL) {
08915          cm_msg(MERROR, "rpc_client_call", "not enough memory to allocate network buffer");
08916          return RPC_EXCEED_BUFFER;
08917       }
08918       _net_send_buffer_size = NET_BUFFER_SIZE;
08919    }
08920 
08921    nc = (NET_COMMAND *) _net_send_buffer;
08922    nc->header.routine_id = routine_id;
08923 
08924    if (transport == RPC_FTCP)
08925       nc->header.routine_id |= TCP_FAST;
08926 
08927    for (i = 0;; i++)
08928       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
08929          break;
08930    rpc_index = i;
08931    if (rpc_list[i].id == 0) {
08932       sprintf(str, "invalid rpc ID (%d)", routine_id);
08933       cm_msg(MERROR, "rpc_client_call", str);
08934       return RPC_INVALID_ID;
08935    }
08936 
08937    /* examine variable argument list and convert it to parameter array */
08938    va_start(ap, routine_id);
08939 
08940    /* find out if we are on a big endian system */
08941    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
08942 
08943    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
08944       tid = rpc_list[rpc_index].param[i].tid;
08945       flags = rpc_list[rpc_index].param[i].flags;
08946 
08947       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
08948           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
08949           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
08950 
08951       if (bpointer)
08952          arg_type = TID_ARRAY;
08953       else
08954          arg_type = tid;
08955 
08956       /* floats are passed as doubles, at least under NT */
08957       if (tid == TID_FLOAT && !bpointer)
08958          arg_type = TID_DOUBLE;
08959 
08960       /* get pointer to argument */
08961       rpc_va_arg(&ap, arg_type, arg);
08962 
08963       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
08964       if (bbig) {
08965          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
08966             arg[0] = arg[3];
08967          }
08968          if (tid == TID_WORD || tid == TID_SHORT) {
08969             arg[0] = arg[2];
08970             arg[1] = arg[3];
08971          }
08972       }
08973 
08974       if (flags & RPC_IN) {
08975          if (bpointer)
08976             arg_size = tid_size[tid];
08977          else
08978             arg_size = tid_size[arg_type];
08979 
08980          /* for strings, the argument size depends on the string length */
08981          if (tid == TID_STRING || tid == TID_LINK)
08982             arg_size = 1 + strlen((char *) *((char **) arg));
08983 
08984          /* for varibale length arrays, the size is given by
08985             the next parameter on the stack */
08986          if (flags & RPC_VARARRAY) {
08987             memcpy(&aptmp, &ap, sizeof(ap));
08988             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
08989 
08990             if (flags & RPC_OUT)
08991                arg_size = *((INT *) * ((void **) arg_tmp));
08992             else
08993                arg_size = *((INT *) arg_tmp);
08994 
08995             *((INT *) param_ptr) = ALIGN8(arg_size);
08996             param_ptr += ALIGN8(sizeof(INT));
08997          }
08998 
08999          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09000             arg_size = rpc_list[rpc_index].param[i].n;
09001 
09002          /* always align parameter size */
09003          param_size = ALIGN8(arg_size);
09004 
09005          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > NET_BUFFER_SIZE) {
09006             cm_msg(MERROR, "rpc_client_call",
09007                    "parameters (%d) too large for network buffer (%d)",
09008                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, NET_BUFFER_SIZE);
09009             return RPC_EXCEED_BUFFER;
09010          }
09011 
09012          if (bpointer)
09013             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09014          else {
09015             /* floats are passed as doubles on most systems */
09016             if (tid != TID_FLOAT)
09017                memcpy(param_ptr, arg, arg_size);
09018             else
09019                *((float *) param_ptr) = (float) *((double *) arg);
09020          }
09021 
09022          param_ptr += param_size;
09023 
09024       }
09025    }
09026 
09027    va_end(ap);
09028 
09029    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09030 
09031    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09032 
09033    /* in FAST TCP mode, only send call and return immediately */
09034    if (transport == RPC_FTCP) {
09035       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09036 
09037       if (i != send_size) {
09038          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09039          return RPC_NET_ERROR;
09040       }
09041 
09042       return RPC_SUCCESS;
09043    }
09044 
09045    /* in TCP mode, send and wait for reply on send socket */
09046    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09047    if (i != send_size) {
09048       cm_msg(MERROR, "rpc_client_call",
09049              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09050              rpc_list[rpc_index].name, _client_connection[index].host_name);
09051       return RPC_NET_ERROR;
09052    }
09053 
09054    /* make some timeout checking */
09055    if (rpc_timeout > 0) {
09056       FD_ZERO(&readfds);
09057       FD_SET(send_sock, &readfds);
09058 
09059       timeout.tv_sec = rpc_timeout / 1000;
09060       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09061 
09062       do {
09063          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09064 
09065          /* if an alarm signal was cought, restart select with reduced timeout */
09066          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09067             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09068 
09069       } while (status == -1);   /* dont return if an alarm signal was cought */
09070 
09071       if (!FD_ISSET(send_sock, &readfds)) {
09072          cm_msg(MERROR, "rpc_client_call", "rpc timeout, routine = \"%s\", host = \"%s\"",
09073                 rpc_list[rpc_index].name, _client_connection[index].host_name);
09074 
09075          /* disconnect to avoid that the reply to this rpc_call comes at
09076             the next rpc_call */
09077          rpc_client_disconnect(hConn, FALSE);
09078 
09079          return RPC_TIMEOUT;
09080       }
09081    }
09082 
09083    /* receive result on send socket */
09084    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09085 
09086    if (i <= 0) {
09087       cm_msg(MERROR, "rpc_client_call",
09088              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09089              rpc_list[rpc_index].name, _client_connection[index].host_name);
09090       return RPC_NET_ERROR;
09091    }
09092 
09093    /* extract result variables and place it to argument list */
09094    status = nc->header.routine_id;
09095 
09096    va_start(ap, routine_id);
09097 
09098    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09099       tid = rpc_list[rpc_index].param[i].tid;
09100       flags = rpc_list[rpc_index].param[i].flags;
09101 
09102       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09103           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09104           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09105 
09106       if (bpointer)
09107          arg_type = TID_ARRAY;
09108       else
09109          arg_type = rpc_list[rpc_index].param[i].tid;
09110 
09111       if (tid == TID_FLOAT && !bpointer)
09112          arg_type = TID_DOUBLE;
09113 
09114       rpc_va_arg(&ap, arg_type, arg);
09115 
09116       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09117          tid = rpc_list[rpc_index].param[i].tid;
09118          flags = rpc_list[rpc_index].param[i].flags;
09119 
09120          arg_size = tid_size[tid];
09121 
09122          if (tid == TID_STRING || tid == TID_LINK)
09123             arg_size = strlen((char *) (param_ptr)) + 1;
09124 
09125          if (flags & RPC_VARARRAY) {
09126             arg_size = *((INT *) param_ptr);
09127             param_ptr += ALIGN8(sizeof(INT));
09128          }
09129 
09130          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09131             arg_size = rpc_list[rpc_index].param[i].n;
09132 
09133          /* return parameters are always pointers */
09134          if (*((char **) arg))
09135             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09136 
09137          /* parameter size is always aligned */
09138          param_size = ALIGN8(arg_size);
09139 
09140          param_ptr += param_size;
09141       }
09142    }
09143 
09144    va_end(ap);
09145 
09146    return status;
09147 }
09148 
09149 
09150 /********************************************************************/
09151 INT rpc_call(const INT routine_id, ...)
09152 /********************************************************************\
09153 
09154   Routine: rpc_call
09155 
09156   Purpose: Call a function on a MIDAS server
09157 
09158   Input:
09159     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09160 
09161     ...                     variable argument list
09162 
09163   Output:
09164     (depends on argument list)
09165 
09166   Function value:
09167     RPC_SUCCESS             Successful completion
09168     RPC_NET_ERROR           Error in socket call
09169     RPC_NO_CONNECTION       No active connection
09170     RPC_TIMEOUT             Timeout in RPC call
09171     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09172     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09173 
09174 \********************************************************************/
09175 {
09176    va_list ap, aptmp;
09177    char arg[8], arg_tmp[8];
09178    INT arg_type, transport, rpc_timeout;
09179    INT i, index, status;
09180    INT param_size, arg_size, send_size;
09181    INT tid, flags;
09182    fd_set readfds;
09183    struct timeval timeout;
09184    char *param_ptr, str[80];
09185    BOOL bpointer, bbig;
09186    NET_COMMAND *nc;
09187    int send_sock;
09188 
09189    send_sock = _server_connection.send_sock;
09190    transport = _server_connection.transport;
09191    rpc_timeout = _server_connection.rpc_timeout;
09192 
09193    /* init network buffer */
09194    if (_net_send_buffer_size == 0) {
09195       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09196       if (_net_send_buffer == NULL) {
09197          cm_msg(MERROR, "rpc_call", "not enough memory to allocate network buffer");
09198          return RPC_EXCEED_BUFFER;
09199       }
09200       _net_send_buffer_size = NET_BUFFER_SIZE;
09201    }
09202 
09203    nc = (NET_COMMAND *) _net_send_buffer;
09204    nc->header.routine_id = routine_id;
09205 
09206    if (transport == RPC_FTCP)
09207       nc->header.routine_id |= TCP_FAST;
09208 
09209    for (i = 0;; i++)
09210       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09211          break;
09212    index = i;
09213    if (rpc_list[i].id == 0) {
09214       sprintf(str, "invalid rpc ID (%d)", routine_id);
09215       cm_msg(MERROR, "rpc_call", str);
09216       return RPC_INVALID_ID;
09217    }
09218 
09219    /* examine variable argument list and convert it to parameter array */
09220    va_start(ap, routine_id);
09221 
09222    /* find out if we are on a big endian system */
09223    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09224 
09225    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09226       tid = rpc_list[index].param[i].tid;
09227       flags = rpc_list[index].param[i].flags;
09228 
09229       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09230           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09231           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09232 
09233       if (bpointer)
09234          arg_type = TID_ARRAY;
09235       else
09236          arg_type = tid;
09237 
09238       /* floats are passed as doubles, at least under NT */
09239       if (tid == TID_FLOAT && !bpointer)
09240          arg_type = TID_DOUBLE;
09241 
09242       /* get pointer to argument */
09243       rpc_va_arg(&ap, arg_type, arg);
09244 
09245       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09246       if (bbig) {
09247          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09248             arg[0] = arg[3];
09249          }
09250          if (tid == TID_WORD || tid == TID_SHORT) {
09251             arg[0] = arg[2];
09252             arg[1] = arg[3];
09253          }
09254       }
09255 
09256       if (flags & RPC_IN) {
09257          if (bpointer)
09258             arg_size = tid_size[tid];
09259          else
09260             arg_size = tid_size[arg_type];
09261 
09262          /* for strings, the argument size depends on the string length */
09263          if (tid == TID_STRING || tid == TID_LINK)
09264             arg_size = 1 + strlen((char *) *((char **) arg));
09265 
09266          /* for varibale length arrays, the size is given by
09267             the next parameter on the stack */
09268          if (flags & RPC_VARARRAY) {
09269             memcpy(&aptmp, &ap, sizeof(ap));
09270             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09271 
09272             if (flags & RPC_OUT)
09273                arg_size = *((INT *) * ((void **) arg_tmp));
09274             else
09275                arg_size = *((INT *) arg_tmp);
09276 
09277             *((INT *) param_ptr) = ALIGN8(arg_size);
09278             param_ptr += ALIGN8(sizeof(INT));
09279          }
09280 
09281          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09282             arg_size = rpc_list[index].param[i].n;
09283 
09284          /* always align parameter size */
09285          param_size = ALIGN8(arg_size);
09286 
09287          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > NET_BUFFER_SIZE) {
09288             cm_msg(MERROR, "rpc_call",
09289                    "parameters (%d) too large for network buffer (%d)",
09290                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, NET_BUFFER_SIZE);
09291             return RPC_EXCEED_BUFFER;
09292          }
09293 
09294          if (bpointer)
09295             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09296          else {
09297             /* floats are passed as doubles on most systems */
09298             if (tid != TID_FLOAT)
09299                memcpy(param_ptr, arg, arg_size);
09300             else
09301                *((float *) param_ptr) = (float) *((double *) arg);
09302          }
09303 
09304          param_ptr += param_size;
09305 
09306       }
09307    }
09308 
09309    va_end(ap);
09310 
09311    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09312 
09313    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09314 
09315    /* in FAST TCP mode, only send call and return immediately */
09316    if (transport == RPC_FTCP) {
09317       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09318 
09319       if (i != send_size) {
09320          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09321          return RPC_NET_ERROR;
09322       }
09323 
09324       return RPC_SUCCESS;
09325    }
09326 
09327    /* in TCP mode, send and wait for reply on send socket */
09328    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09329    if (i != send_size) {
09330       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09331       return RPC_NET_ERROR;
09332    }
09333 
09334    /* make some timeout checking */
09335    if (rpc_timeout > 0) {
09336       FD_ZERO(&readfds);
09337       FD_SET(send_sock, &readfds);
09338 
09339       timeout.tv_sec = rpc_timeout / 1000;
09340       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09341 
09342       do {
09343          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09344 
09345          /* if an alarm signal was cought, restart select with reduced timeout */
09346          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09347             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09348 
09349       } while (status == -1);   /* dont return if an alarm signal was cought */
09350 
09351       if (!FD_ISSET(send_sock, &readfds)) {
09352          cm_msg(MERROR, "rpc_call", "rpc timeout, routine = \"%s\"", rpc_list[index].name);
09353 
09354          /* disconnect to avoid that the reply to this rpc_call comes at
09355             the next rpc_call */
09356          rpc_server_disconnect();
09357 
09358          return RPC_TIMEOUT;
09359       }
09360    }
09361 
09362    /* receive result on send socket */
09363    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09364 
09365    if (i <= 0) {
09366       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"", rpc_list[index].name);
09367       return RPC_NET_ERROR;
09368    }
09369 
09370    /* extract result variables and place it to argument list */
09371    status = nc->header.routine_id;
09372 
09373    va_start(ap, routine_id);
09374 
09375    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09376       tid = rpc_list[index].param[i].tid;
09377       flags = rpc_list[index].param[i].flags;
09378 
09379       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09380           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09381           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09382 
09383       if (bpointer)
09384          arg_type = TID_ARRAY;
09385       else
09386          arg_type = rpc_list[index].param[i].tid;
09387 
09388       if (tid == TID_FLOAT && !bpointer)
09389          arg_type = TID_DOUBLE;
09390 
09391       rpc_va_arg(&ap, arg_type, arg);
09392 
09393       if (rpc_list[index].param[i].flags & RPC_OUT) {
09394          tid = rpc_list[index].param[i].tid;
09395          arg_size = tid_size[tid];
09396 
09397          if (tid == TID_STRING || tid == TID_LINK)
09398             arg_size = strlen((char *) (param_ptr)) + 1;
09399 
09400          if (flags & RPC_VARARRAY) {
09401             arg_size = *((INT *) param_ptr);
09402             param_ptr += ALIGN8(sizeof(INT));
09403          }
09404 
09405          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09406             arg_size = rpc_list[index].param[i].n;
09407 
09408          /* return parameters are always pointers */
09409          if (*((char **) arg))
09410             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09411 
09412          /* parameter size is always aligned */
09413          param_size = ALIGN8(arg_size);
09414 
09415          param_ptr += param_size;
09416       }
09417    }
09418 
09419    va_end(ap);
09420 
09421    return status;
09422 }
09423 
09424 
09425 /********************************************************************/
09426 INT rpc_set_opt_tcp_size(INT tcp_size)
09427 {
09428    INT old;
09429 
09430    old = _opt_tcp_size;
09431    _opt_tcp_size = tcp_size;
09432    return old;
09433 }
09434 
09435 INT rpc_get_opt_tcp_size()
09436 {
09437    return _opt_tcp_size;
09438 }
09439 
09440 /**dox***************************************************************/
09441 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09442 
09443 /********************************************************************/
09444 /**
09445 Fast send_event routine which bypasses the RPC layer and
09446            sends the event directly at the TCP level.
09447 @param buffer_handle      Handle of the buffer to send the event to.
09448                           Must be obtained via bm_open_buffer.
09449 @param source             Address of the event to send. It must have
09450                           a proper event header.
09451 @param buf_size           Size of event in bytes with header.
09452 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
09453                           function returns immediately if it cannot
09454                           send the event over the network. In SYNC
09455                           mode, it waits until the packet is sent
09456                           (blocking).
09457 @param mode               Determines in which mode the event is sent.
09458                           If zero, use RPC socket, if one, use special
09459                           event socket to bypass RPC layer on the
09460                           server side.
09461 
09462 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR,
09463         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER
09464 */
09465 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag, INT mode)
09466 {
09467    INT i;
09468    NET_COMMAND *nc;
09469    unsigned long flag;
09470    BOOL would_block = 0;
09471    DWORD aligned_buf_size;
09472 
09473    aligned_buf_size = ALIGN8(buf_size);
09474    _rpc_sock = mode == 0 ? _server_connection.send_sock : _server_connection.event_sock;
09475 
09476    if (aligned_buf_size != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
09477       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
09478       return BM_INVALID_PARAM;
09479    }
09480    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
09481       cm_msg(MERROR, "rpc_send_event",
09482              "event size (%d) larger than maximum event size (%d)",
09483              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
09484       return RPC_EXCEED_BUFFER;
09485    }
09486 
09487    if (!rpc_is_remote())
09488       return bm_send_event(buffer_handle, source, buf_size, async_flag);
09489 
09490    /* init network buffer */
09491    if (!_tcp_buffer)
09492       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
09493    if (!_tcp_buffer) {
09494       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
09495       return RPC_EXCEED_BUFFER;
09496    }
09497 
09498    /* check if not enough space in TCP buffer */
09499    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
09500        (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
09501       /* set socket to nonblocking IO */
09502       if (async_flag == ASYNC) {
09503          flag = 1;
09504 #ifdef OS_VXWORKS
09505          ioctlsocket(_rpc_sock, FIONBIO, (int) &flag);
09506 #else
09507          ioctlsocket(_rpc_sock, FIONBIO, &flag);
09508 #endif
09509       }
09510 
09511       i = send_tcp(_rpc_sock, _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09512 
09513       if (i < 0)
09514 #ifdef OS_WINNT
09515          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
09516 #else
09517          would_block = (errno == EWOULDBLOCK);
09518 #endif
09519 
09520       /* set socket back to blocking IO */
09521       if (async_flag == ASYNC) {
09522          flag = 0;
09523 #ifdef OS_VXWORKS
09524          ioctlsocket(_rpc_sock, FIONBIO, (int) &flag);
09525 #else
09526          ioctlsocket(_rpc_sock, FIONBIO, &flag);
09527 #endif
09528       }
09529 
09530       /* increment read pointer */
09531       if (i > 0)
09532          _tcp_rp += i;
09533 
09534       /* check if whole buffer is sent */
09535       if (_tcp_rp == _tcp_wp)
09536          _tcp_rp = _tcp_wp = 0;
09537 
09538       if (i < 0 && !would_block) {
09539          printf("send_tcp() returned %d\n", i);
09540          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed");
09541          return RPC_NET_ERROR;
09542       }
09543 
09544       /* return if buffer is not emptied */
09545       if (_tcp_wp > 0)
09546          return BM_ASYNC_RETURN;
09547    }
09548 
09549    if (mode == 0) {
09550       nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
09551       nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
09552       nc->header.param_size = 4 * 8 + aligned_buf_size;
09553 
09554       /* assemble parameters manually */
09555       *((INT *) (&nc->param[0])) = buffer_handle;
09556       *((INT *) (&nc->param[8])) = buf_size;
09557 
09558       /* send events larger than optimal buffer size directly */
09559       if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
09560          /* send header */
09561          send_tcp(_rpc_sock, _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
09562 
09563          /* send data */
09564          send_tcp(_rpc_sock, (char *) source, aligned_buf_size, 0);
09565 
09566          /* send last two parameters */
09567          *((INT *) (&nc->param[0])) = buf_size;
09568          *((INT *) (&nc->param[8])) = 0;
09569          send_tcp(_rpc_sock, &nc->param[0], 16, 0);
09570       } else {
09571          /* copy event */
09572          memcpy(&nc->param[16], source, buf_size);
09573 
09574          /* last two parameters (buf_size and async_flag */
09575          *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
09576          *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
09577 
09578          _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09579       }
09580 
09581    } else {
09582 
09583       /* send events larger than optimal buffer size directly */
09584       if (aligned_buf_size + 4 * 8 + sizeof(INT) >= (DWORD) _opt_tcp_size) {
09585          /* send buffer */
09586          send_tcp(_rpc_sock, (char *) &buffer_handle, sizeof(INT), 0);
09587 
09588          /* send data */
09589          send_tcp(_rpc_sock, (char *) source, aligned_buf_size, 0);
09590       } else {
09591          /* copy event */
09592          *((INT *) (_tcp_buffer + _tcp_wp)) = buffer_handle;
09593          _tcp_wp += sizeof(INT);
09594          memcpy(_tcp_buffer + _tcp_wp, source, buf_size);
09595 
09596          _tcp_wp += aligned_buf_size;
09597       }
09598    }
09599 
09600    return RPC_SUCCESS;
09601 }
09602 
09603 
09604 /**dox***************************************************************/
09605 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09606 
09607 /********************************************************************/
09608 int rpc_get_send_sock()
09609 /********************************************************************\
09610 
09611   Routine: rpc_get_send_sock
09612 
09613   Purpose: Return send socket to MIDAS server. Used by MFE.C for
09614            optimized event sending.
09615 
09616   Input:
09617     none
09618 
09619   Output:
09620     none
09621 
09622   Function value:
09623     int    socket
09624 
09625 \********************************************************************/
09626 {
09627    return _server_connection.send_sock;
09628 }
09629 
09630 
09631 /********************************************************************/
09632 int rpc_get_event_sock()
09633 /********************************************************************\
09634 
09635   Routine: rpc_get_event_sock
09636 
09637   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
09638            optimized event sending.
09639 
09640   Input:
09641     none
09642 
09643   Output:
09644     none
09645 
09646   Function value:
09647     int    socket
09648 
09649 \********************************************************************/
09650 {
09651    return _server_connection.event_sock;
09652 }
09653 
09654 /**dox***************************************************************/
09655 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09656 
09657 /********************************************************************/
09658 /**
09659 Send event residing in the TCP cache buffer filled by
09660            rpc_send_event. This routine should be called when a
09661            run is stopped.
09662 
09663 @return RPC_SUCCESS, RPC_NET_ERROR
09664 */
09665 INT rpc_flush_event()
09666 {
09667    INT i;
09668 
09669    if (!rpc_is_remote())
09670       return RPC_SUCCESS;
09671 
09672    /* return if rpc_send_event was not called */
09673    if (!_tcp_buffer || _tcp_wp == 0)
09674       return RPC_SUCCESS;
09675 
09676    /* empty TCP buffer */
09677    if (_tcp_wp > 0) {
09678       i = send_tcp(_rpc_sock, _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09679 
09680       if (i != _tcp_wp - _tcp_rp) {
09681          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
09682          return RPC_NET_ERROR;
09683       }
09684    }
09685 
09686    _tcp_rp = _tcp_wp = 0;
09687 
09688    return RPC_SUCCESS;
09689 }
09690 
09691 /**dox***************************************************************/
09692 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09693 
09694 /********************************************************************/
09695 
09696 typedef struct {
09697    int transition;
09698    int run_number;
09699    time_t trans_time;
09700    int sequence_number;
09701 } TR_FIFO;
09702 
09703 static TR_FIFO tr_fifo[10];
09704 static int trf_wp, trf_rp;
09705 
09706 static INT rpc_transition_dispatch(INT index, void *prpc_param[])
09707 /********************************************************************\
09708 
09709   Routine: rpc_transition_dispatch
09710 
09711   Purpose: Gets called when a transition function was registered and
09712            a transition occured. Internal use only.
09713 
09714   Input:
09715     INT    index            RPC function ID
09716     void   *prpc_param      RPC parameters
09717 
09718   Output:
09719     none
09720 
09721   Function value:
09722     INT    return value from called user routine
09723 
09724 \********************************************************************/
09725 {
09726    INT status, i;
09727 
09728    /* erase error string */
09729    *(CSTRING(2)) = 0;
09730 
09731    if (index == RPC_RC_TRANSITION) {
09732       for (i = 0; i < MAX_TRANSITIONS; i++)
09733          if (_trans_table[i].transition == CINT(0) && _trans_table[i].sequence_number == CINT(4))
09734             break;
09735 
09736       /* call registerd function */
09737       if (i < MAX_TRANSITIONS) {
09738          if (_trans_table[i].func)
09739             /* execute callback if defined */
09740             status = _trans_table[i].func(CINT(1), CSTRING(2));
09741          else {
09742             /* store transition in FIFO */
09743             tr_fifo[trf_wp].transition = CINT(0);
09744             tr_fifo[trf_wp].run_number = CINT(1);
09745             tr_fifo[trf_wp].trans_time = time(NULL);
09746             tr_fifo[trf_wp].sequence_number = CINT(4);
09747             trf_wp = (trf_wp + 1) % 10;
09748             status = RPC_SUCCESS;
09749          }
09750       } else
09751          status = RPC_SUCCESS;
09752 
09753    } else {
09754       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
09755       status = RPC_INVALID_ID;
09756    }
09757 
09758    return status;
09759 }
09760 
09761 /********************************************************************/
09762 int cm_query_transition(int *transition, int *run_number, int *trans_time)
09763 /********************************************************************\
09764 
09765   Routine: cm_query_transition
09766 
09767   Purpose: Query system if transition has occured. Normally, one
09768            registers callbacks for transitions via
09769            cm_register_transition. In some environments however,
09770            callbacks are not possible. In that case one spciefies
09771            a NULL pointer as the callback routine and can query
09772            transitions "manually" by calling this functions. A small
09773            FIFO takes care that no transition is lost if this functions
09774            did not get called between some transitions.
09775 
09776   Output:
09777     INT   *transition        Type of transition, one of TR_xxx
09778     INT   *run_nuber         Run number for transition
09779     time_t *trans_time       Time (in UNIX time) of transition
09780 
09781   Function value:
09782     FALSE  No transition occured since last call
09783     TRUE   Transition occured
09784 
09785 \********************************************************************/
09786 {
09787 
09788    if (trf_wp == trf_rp)
09789       return FALSE;
09790 
09791    if (transition)
09792       *transition = tr_fifo[trf_rp].transition;
09793 
09794    if (run_number)
09795       *run_number = tr_fifo[trf_rp].run_number;
09796 
09797    if (trans_time)
09798       *trans_time = (int) tr_fifo[trf_rp].trans_time;
09799 
09800    trf_rp = (trf_rp + 1) % 10;
09801 
09802    return TRUE;
09803 }
09804 
09805 /********************************************************************\
09806 *                        server functions                            *
09807 \********************************************************************/
09808 
09809 #if 0
09810 void debug_dump(unsigned char *p, int size)
09811 {
09812    int i, j;
09813    unsigned char c;
09814 
09815    for (i = 0; i < (size-1)/16+1; i++) {
09816       printf("%p ", p + i * 16);
09817       for (j = 0; j < 16; j++)
09818          if (i * 16 + j < size)
09819             printf("%02X ", p[i * 16 + j]);
09820          else
09821             printf("   ");
09822       printf(" ");
09823 
09824       for (j = 0; j < 16; j++) {
09825          c = p[i * 16 + j];
09826          if (i * 16 + j < size)
09827             printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
09828       }
09829       printf("\n");
09830    }
09831 
09832    printf("\n");
09833 }
09834 #endif
09835 
09836 /********************************************************************/
09837 INT recv_tcp_server(INT index, char *buffer, DWORD buffer_size, INT flags, INT * remaining)
09838 /********************************************************************\
09839 
09840   Routine: recv_tcp_server
09841 
09842   Purpose: TCP receive routine with local cache. To speed up network
09843            performance, a 64k buffer is read in at once and split into
09844            several RPC command on successive calls to recv_tcp_server.
09845            Therefore, the number of recv() calls is minimized.
09846 
09847            This routine is ment to be called by the server process.
09848            Clients should call recv_tcp instead.
09849 
09850   Input:
09851     INT   index              Index of server connection
09852     DWORD buffer_size        Size of the buffer in bytes.
09853     INT   flags              Flags passed to recv()
09854     INT   convert_flags      Convert flags needed for big/little
09855                              endian conversion
09856 
09857   Output:
09858     char  *buffer            Network receive buffer.
09859     INT   *remaining         Remaining data in cache
09860 
09861   Function value:
09862     INT                      Same as recv()
09863 
09864 \********************************************************************/
09865 {
09866    INT size, param_size;
09867    NET_COMMAND *nc;
09868    INT write_ptr, read_ptr, misalign;
09869    char *net_buffer;
09870    INT copied, status;
09871    INT sock;
09872 
09873    sock = _server_acception[index].recv_sock;
09874 
09875    if (flags & MSG_PEEK) {
09876       status = recv(sock, buffer, buffer_size, flags);
09877       if (status == -1)
09878          cm_msg(MERROR, "recv_tcp_server",
09879                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status, errno, strerror(errno));
09880       return status;
09881    }
09882 
09883    if (!_server_acception[index].net_buffer) {
09884       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
09885          _server_acception[index].net_buffer_size = NET_TCP_SIZE;
09886       else
09887          _server_acception[index].net_buffer_size = NET_BUFFER_SIZE;
09888 
09889       _server_acception[index].net_buffer = (char *) M_MALLOC(_server_acception[index].net_buffer_size);
09890       _server_acception[index].write_ptr = 0;
09891       _server_acception[index].read_ptr = 0;
09892       _server_acception[index].misalign = 0;
09893    }
09894    if (!_server_acception[index].net_buffer) {
09895       cm_msg(MERROR, "recv_tcp_server", "not enough memory to allocate network buffer");
09896       return -1;
09897    }
09898 
09899    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
09900       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
09901       return -1;
09902    }
09903 
09904    copied = 0;
09905    param_size = -1;
09906 
09907    write_ptr = _server_acception[index].write_ptr;
09908    read_ptr = _server_acception[index].read_ptr;
09909    misalign = _server_acception[index].misalign;
09910    net_buffer = _server_acception[index].net_buffer;
09911 
09912    do {
09913       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
09914          if (param_size == -1) {
09915             if (copied > 0) {
09916                /* assemble split header */
09917                memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
09918                nc = (NET_COMMAND *) (buffer);
09919             } else
09920                nc = (NET_COMMAND *) (net_buffer + read_ptr);
09921 
09922             param_size = (INT) nc->header.param_size;
09923 
09924             if (_server_acception[index].convert_flags)
09925                rpc_convert_single(&param_size, TID_DWORD, 0, _server_acception[index].convert_flags);
09926          }
09927 
09928          /* check if parameters fit in buffer */
09929          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
09930             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
09931             _server_acception[index].read_ptr = _server_acception[index].write_ptr = 0;
09932             return -1;
09933          }
09934 
09935          /* check if we have all parameters in buffer */
09936          if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
09937             break;
09938       }
09939 
09940       /* not enough data, so copy partially and get new */
09941       size = write_ptr - read_ptr;
09942 
09943       if (size > 0) {
09944          memcpy(buffer + copied, net_buffer + read_ptr, size);
09945          copied += size;
09946          read_ptr = write_ptr;
09947       }
09948 #ifdef OS_UNIX
09949       do {
09950          write_ptr = recv(sock, net_buffer + misalign, _server_acception[index].net_buffer_size - 8, flags);
09951 
09952          /* don't return if an alarm signal was cought */
09953       } while (write_ptr == -1 && errno == EINTR);
09954 #else
09955       write_ptr = recv(sock, net_buffer + misalign, _server_acception[index].net_buffer_size - 8, flags);
09956 #endif
09957 
09958       /* abort if connection broken */
09959       if (write_ptr <= 0) {
09960          cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)",
09961                 write_ptr, errno, strerror(errno));
09962 
09963          if (remaining)
09964             *remaining = 0;
09965 
09966          return write_ptr;
09967       }
09968 
09969       read_ptr = misalign;
09970       write_ptr += misalign;
09971 
09972       misalign = write_ptr % 8;
09973    } while (TRUE);
09974 
09975    /* copy rest of parameters */
09976    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
09977    memcpy(buffer + copied, net_buffer + read_ptr, size);
09978    read_ptr += size;
09979 
09980    if (remaining) {
09981       /* don't keep rpc_server_receive in an infinite loop */
09982       if (write_ptr - read_ptr < param_size)
09983          *remaining = 0;
09984       else
09985          *remaining = write_ptr - read_ptr;
09986    }
09987 
09988    _server_acception[index].write_ptr = write_ptr;
09989    _server_acception[index].read_ptr = read_ptr;
09990    _server_acception[index].misalign = misalign;
09991 
09992    return size + copied;
09993 }
09994 
09995 
09996 /********************************************************************/
09997 INT recv_tcp_check(int sock)
09998 /********************************************************************\
09999 
10000   Routine: recv_tcp_check
10001 
10002   Purpose: Check if in TCP receive buffer associated with sock is
10003            some data. Called by ss_suspend.
10004 
10005   Input:
10006     INT   sock               TCP receive socket
10007 
10008   Output:
10009     none
10010 
10011   Function value:
10012     INT   count              Number of bytes remaining in TCP buffer
10013 
10014 \********************************************************************/
10015 {
10016    INT index;
10017 
10018    /* figure out to which connection socket belongs */
10019    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10020       if (_server_acception[index].recv_sock == sock)
10021          break;
10022 
10023    return _server_acception[index].write_ptr - _server_acception[index].read_ptr;
10024 }
10025 
10026 
10027 /********************************************************************/
10028 INT recv_event_server(INT index, char *buffer, DWORD buffer_size, INT flags, INT * remaining)
10029 /********************************************************************\
10030 
10031   Routine: recv_event_server
10032 
10033   Purpose: TCP event receive routine with local cache. To speed up
10034            network performance, a 64k buffer is read in at once and
10035            split into several RPC command on successive calls to
10036            recv_event_server. Therefore, the number of recv() calls
10037            is minimized.
10038 
10039            This routine is ment to be called by the server process.
10040            Clients should call recv_tcp instead.
10041 
10042   Input:
10043     INT   index              Index of server connection
10044     DWORD buffer_size        Size of the buffer in bytes.
10045     INT   flags              Flags passed to recv()
10046     INT   convert_flags      Convert flags needed for big/little
10047                              endian conversion
10048 
10049   Output:
10050     char  *buffer            Network receive buffer.
10051     INT   *remaining         Remaining data in cache
10052 
10053   Function value:
10054     INT                      Same as recv()
10055 
10056 \********************************************************************/
10057 {
10058    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10059    EVENT_HEADER *pevent;
10060    INT write_ptr, read_ptr, misalign;
10061    char *net_buffer;
10062    INT copied, status;
10063    INT sock;
10064    RPC_SERVER_ACCEPTION *psa;
10065 
10066    psa = &_server_acception[index];
10067    sock = psa->event_sock;
10068 
10069    if (flags & MSG_PEEK) {
10070       status = recv(sock, buffer, buffer_size, flags);
10071       if (status == -1)
10072          cm_msg(MERROR, "recv_event_server",
10073                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status, errno, strerror(errno));
10074       return status;
10075    }
10076 
10077    if (!psa->ev_net_buffer) {
10078       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10079          psa->net_buffer_size = NET_TCP_SIZE;
10080       else
10081          psa->net_buffer_size = NET_BUFFER_SIZE;
10082 
10083       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10084       psa->ev_write_ptr = 0;
10085       psa->ev_read_ptr = 0;
10086       psa->ev_misalign = 0;
10087    }
10088    if (!psa->ev_net_buffer) {
10089       cm_msg(MERROR, "recv_event_server", "not enough memory to allocate network buffer");
10090       return -1;
10091    }
10092 
10093    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10094 
10095    if ((INT) buffer_size < header_size) {
10096       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10097       return -1;
10098    }
10099 
10100    copied = 0;
10101    event_size = -1;
10102 
10103    write_ptr = psa->ev_write_ptr;
10104    read_ptr = psa->ev_read_ptr;
10105    misalign = psa->ev_misalign;
10106    net_buffer = psa->ev_net_buffer;
10107 
10108    do {
10109       if (write_ptr - read_ptr >= header_size - copied) {
10110          if (event_size == -1) {
10111             if (copied > 0) {
10112                /* assemble split header */
10113                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10114                pbh = (INT *) buffer;
10115             } else
10116                pbh = (INT *) (net_buffer + read_ptr);
10117 
10118             pevent = (EVENT_HEADER *) (pbh + 1);
10119 
10120             event_size = pevent->data_size;
10121             if (psa->convert_flags)
10122                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10123 
10124             aligned_event_size = ALIGN8(event_size);
10125          }
10126 
10127          /* check if data part fits in buffer */
10128          if ((INT) buffer_size < aligned_event_size + header_size) {
10129             cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10130             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10131             return -1;
10132          }
10133 
10134          /* check if we have whole event in buffer */
10135          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10136             break;
10137       }
10138 
10139       /* not enough data, so copy partially and get new */
10140       size = write_ptr - read_ptr;
10141 
10142       if (size > 0) {
10143          memcpy(buffer + copied, net_buffer + read_ptr, size);
10144          copied += size;
10145          read_ptr = write_ptr;
10146       }
10147 #ifdef OS_UNIX
10148       do {
10149          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10150 
10151          /* don't return if an alarm signal was cought */
10152       } while (write_ptr == -1 && errno == EINTR);
10153 #else
10154       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10155 #endif
10156 
10157       /* abort if connection broken */
10158       if (write_ptr <= 0) {
10159          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)",
10160                 write_ptr, errno, strerror(errno));
10161 
10162          if (remaining)
10163             *remaining = 0;
10164 
10165          return write_ptr;
10166       }
10167 
10168       read_ptr = misalign;
10169       write_ptr += misalign;
10170 
10171       misalign = write_ptr % 8;
10172    } while (TRUE);
10173 
10174    /* copy rest of event */
10175    size = aligned_event_size + header_size - copied;
10176    if (size > 0) {
10177       memcpy(buffer + copied, net_buffer + read_ptr, size);
10178       read_ptr += size;
10179    }
10180 
10181    if (remaining)
10182       *remaining = write_ptr - read_ptr;
10183 
10184    psa->ev_write_ptr = write_ptr;
10185    psa->ev_read_ptr = read_ptr;
10186    psa->ev_misalign = misalign;
10187 
10188    /* convert header little endian/big endian */
10189    if (psa->convert_flags) {
10190       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10191 
10192       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10193       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10194       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10195       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10196       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10197       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10198    }
10199 
10200    return header_size + event_size;
10201 }
10202 
10203 
10204 /********************************************************************/
10205 INT recv_event_check(int sock)
10206 /********************************************************************\
10207 
10208   Routine: recv_event_check
10209 
10210   Purpose: Check if in TCP event receive buffer associated with sock
10211            is some data. Called by ss_suspend.
10212 
10213   Input:
10214     INT   sock               TCP receive socket
10215 
10216   Output:
10217     none
10218 
10219   Function value:
10220     INT   count              Number of bytes remaining in TCP buffer
10221 
10222 \********************************************************************/
10223 {
10224    INT index;
10225 
10226    /* figure out to which connection socket belongs */
10227    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10228       if (_server_acception[index].event_sock == sock)
10229          break;
10230 
10231    return _server_acception[index].ev_write_ptr - _server_acception[index].ev_read_ptr;
10232 }
10233 
10234 
10235 /********************************************************************/
10236 INT rpc_register_server(INT server_type, char *name, INT * port, INT(*func) (INT, void **))
10237 /********************************************************************\
10238 
10239   Routine: rpc_register_server
10240 
10241   Purpose: Register the calling process as a MIDAS RPC server. Note
10242            that cm_connnect_experiment must be called prior to any call of
10243            rpc_register_server.
10244 
10245   Input:
10246     INT   server_type       One of the following constants:
10247                             ST_SINGLE: register a single process server
10248                             ST_MTHREAD: for each connection, start
10249                                         a new thread to serve it
10250                             ST_MPROCESS: for each connection, start
10251                                          a new process to server it
10252                             ST_SUBPROCESS: the routine was called from
10253                                            a multi process server
10254                             ST_REMOTE: register a client program server
10255                                        connected to the ODB
10256     char  *name             Name of .EXE file to start in MPROCESS mode
10257     INT   *port             TCP port for listen. NULL if listen as main
10258                             server (MIDAS_TCP_PORT is then used). If *port=0,
10259                             the OS chooses a free port and returns it. If
10260                             *port != 0, this port is used.
10261     INT   *func             Default dispatch function
10262 
10263   Output:
10264     INT   *port             Port under which server is listening.
10265 
10266   Function value:
10267     RPC_SUCCESS             Successful completion
10268     RPC_NET_ERROR           Error in socket call
10269     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10270 
10271 \********************************************************************/
10272 {
10273    struct sockaddr_in bind_addr;
10274    INT status, flag;
10275    unsigned int size;
10276 
10277 #ifdef OS_WINNT
10278    {
10279       WSADATA WSAData;
10280 
10281       /* Start windows sockets */
10282       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10283          return RPC_NET_ERROR;
10284    }
10285 #endif
10286 
10287    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10288 
10289    /* register system functions */
10290    rpc_register_functions(rpc_get_internal_list(0), func);
10291 
10292    if (name != NULL)
10293       rpc_set_server_option(RPC_OSERVER_NAME, (POINTER_T) name);
10294 
10295    /* in subprocess mode, don't start listener */
10296    if (server_type == ST_SUBPROCESS)
10297       return RPC_SUCCESS;
10298 
10299    /* create a socket for listening */
10300    _lsock = socket(AF_INET, SOCK_STREAM, 0);
10301    if (_lsock == -1) {
10302       cm_msg(MERROR, "rpc_register_server", "socket() failed");
10303       return RPC_NET_ERROR;
10304    }
10305 
10306    /* reuse address, needed if previous server stopped (30s timeout!) */
10307    flag = 1;
10308    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
10309    if (status < 0) {
10310       cm_msg(MERROR, "rpc_register_server", "setsockopt() failed");
10311       return RPC_NET_ERROR;
10312    }
10313 
10314    /* bind local node name and port to socket */
10315    memset(&bind_addr, 0, sizeof(bind_addr));
10316    bind_addr.sin_family = AF_INET;
10317    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
10318 
10319    if (!port)
10320       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
10321    else
10322       bind_addr.sin_port = htons((short) (*port));
10323 
10324    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
10325    if (status < 0) {
10326       cm_msg(MERROR, "rpc_register_server", "bind() failed: %s", strerror(errno));
10327       return RPC_NET_ERROR;
10328    }
10329 
10330    /* listen for connection */
10331 #ifdef OS_MSDOS
10332    status = listen(_lsock, 1);
10333 #else
10334    status = listen(_lsock, SOMAXCONN);
10335 #endif
10336    if (status < 0) {
10337       cm_msg(MERROR, "rpc_register_server", "listen() failed");
10338       return RPC_NET_ERROR;
10339    }
10340 
10341    /* return port wich OS has choosen */
10342    if (port && *port == 0) {
10343       size = sizeof(bind_addr);
10344       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
10345       *port = ntohs(bind_addr.sin_port);
10346    }
10347 
10348    /* define callbacks for ss_suspend */
10349    if (server_type == ST_REMOTE)
10350       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
10351    else
10352       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
10353 
10354    return RPC_SUCCESS;
10355 }
10356 
10357 
10358 /********************************************************************/
10359 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
10360 /********************************************************************\
10361 
10362   Routine: rpc_execute
10363 
10364   Purpose: Execute a RPC command received over the network
10365 
10366   Input:
10367     INT  sock               TCP socket to which the result should be
10368                             send back
10369 
10370     char *buffer            Command buffer
10371     INT  convert_flags      Flags for data conversion
10372 
10373   Output:
10374     none
10375 
10376   Function value:
10377     RPC_SUCCESS             Successful completion
10378     RPC_INVALID_ID          Invalid routine_id received
10379     RPC_NET_ERROR           Error in socket call
10380     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10381     RPC_SHUTDOWN            Shutdown requested
10382     SS_ABORT                TCP connection broken
10383     SS_EXIT                 TCP connection closed
10384 
10385 \********************************************************************/
10386 {
10387    INT i, index, routine_id, status;
10388    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10389    INT tid, flags;
10390    NET_COMMAND *nc_in, *nc_out;
10391    INT param_size, max_size;
10392    void *prpc_param[20];
10393    char str[1024], debug_line[1024];
10394 
10395    /* return buffer must be auto for multi-thread servers */
10396    char *return_buffer;
10397 
10398    return_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
10399    assert(return_buffer);
10400 
10401    /* extract pointer array to parameters */
10402    nc_in = (NET_COMMAND *) buffer;
10403    nc_out = (NET_COMMAND *) return_buffer;
10404 
10405    /* convert header format (byte swapping) */
10406    if (convert_flags) {
10407       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
10408       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
10409    }
10410 
10411    /* no result return in FAST TCP mode */
10412    if (nc_in->header.routine_id & TCP_FAST)
10413       sock = 0;
10414 
10415    /* find entry in rpc_list */
10416    routine_id = nc_in->header.routine_id & ~TCP_FAST;
10417 
10418    for (i = 0;; i++)
10419       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
10420          break;
10421    index = i;
10422    if (rpc_list[i].id == 0) {
10423       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
10424       M_FREE(return_buffer);
10425       return RPC_INVALID_ID;
10426    }
10427 
10428    in_param_ptr = nc_in->param;
10429    out_param_ptr = nc_out->param;
10430 
10431    sprintf(debug_line, "%s(", rpc_list[index].name);
10432 
10433    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10434       tid = rpc_list[index].param[i].tid;
10435       flags = rpc_list[index].param[i].flags;
10436 
10437       if (flags & RPC_IN) {
10438          param_size = ALIGN8(tid_size[tid]);
10439 
10440          if (tid == TID_STRING || tid == TID_LINK)
10441             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10442 
10443          if (flags & RPC_VARARRAY) {
10444             /* for arrays, the size is stored as a INT in front of the array */
10445             param_size = *((INT *) in_param_ptr);
10446             if (convert_flags)
10447                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
10448             param_size = ALIGN8(param_size);
10449 
10450             in_param_ptr += ALIGN8(sizeof(INT));
10451          }
10452 
10453          if (tid == TID_STRUCT)
10454             param_size = ALIGN8(rpc_list[index].param[i].n);
10455 
10456          prpc_param[i] = in_param_ptr;
10457 
10458          /* convert data format */
10459          if (convert_flags) {
10460             if (flags & RPC_VARARRAY)
10461                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
10462             else
10463                rpc_convert_data(in_param_ptr, tid, flags,
10464                                 rpc_list[index].param[i].n * tid_size[tid], convert_flags);
10465          }
10466 
10467          db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
10468          if (rpc_list[index].param[i].tid == TID_STRING) {
10469             /* check for long strings (db_create_record...) */
10470             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10471                strcat(debug_line, "\"");
10472                strcat(debug_line, str);
10473                strcat(debug_line, "\"");
10474             } else
10475                strcat(debug_line, "...");
10476          } else
10477             strcat(debug_line, str);
10478 
10479          in_param_ptr += param_size;
10480       }
10481 
10482       if (flags & RPC_OUT) {
10483          param_size = ALIGN8(tid_size[tid]);
10484 
10485          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10486             /* save maximum array length */
10487             max_size = *((INT *) in_param_ptr);
10488             if (convert_flags)
10489                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
10490             max_size = ALIGN8(max_size);
10491 
10492             *((INT *) out_param_ptr) = max_size;
10493 
10494             /* save space for return array length */
10495             out_param_ptr += ALIGN8(sizeof(INT));
10496 
10497             /* use maximum array length from input */
10498             param_size += max_size;
10499          }
10500 
10501          if (rpc_list[index].param[i].tid == TID_STRUCT)
10502             param_size = ALIGN8(rpc_list[index].param[i].n);
10503 
10504          if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > NET_BUFFER_SIZE) {
10505             cm_msg(MERROR, "rpc_execute",
10506                    "return parameters (%d) too large for network buffer (%d)",
10507                    (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, NET_BUFFER_SIZE);
10508             M_FREE(return_buffer);
10509             return RPC_EXCEED_BUFFER;
10510          }
10511 
10512          /* if parameter goes both directions, copy input to output */
10513          if (rpc_list[index].param[i].flags & RPC_IN)
10514             memcpy(out_param_ptr, prpc_param[i], param_size);
10515 
10516          if (_debug_print && !(flags & RPC_IN))
10517             strcat(debug_line, "-");
10518 
10519          prpc_param[i] = out_param_ptr;
10520          out_param_ptr += param_size;
10521       }
10522 
10523       if (rpc_list[index].param[i + 1].tid)
10524          strcat(debug_line, ", ");
10525    }
10526 
10527    strcat(debug_line, ")");
10528    rpc_debug_printf(debug_line);
10529 
10530    last_param_ptr = out_param_ptr;
10531 
10532   /*********************************\
10533   *   call dispatch function        *
10534   \*********************************/
10535    if (rpc_list[index].dispatch)
10536       status = rpc_list[index].dispatch(routine_id, prpc_param);
10537    else
10538       status = RPC_INVALID_ID;
10539 
10540    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
10541       status = RPC_SUCCESS;
10542 
10543    /* return immediately for closed down client connections */
10544    if (!sock && routine_id == RPC_ID_EXIT) {
10545       M_FREE(return_buffer);
10546       return SS_EXIT;
10547    }
10548 
10549    if (!sock && routine_id == RPC_ID_SHUTDOWN) {
10550       M_FREE(return_buffer);
10551       return RPC_SHUTDOWN;
10552    }
10553 
10554    /* Return if TCP connection broken */
10555    if (status == SS_ABORT) {
10556       M_FREE(return_buffer);
10557       return SS_ABORT;
10558    }
10559 
10560    /* if sock == 0, we are in FTCP mode and may not sent results */
10561    if (!sock) {
10562       M_FREE(return_buffer);
10563       return RPC_SUCCESS;
10564    }
10565 
10566    /* compress variable length arrays */
10567    out_param_ptr = nc_out->param;
10568    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10569       if (rpc_list[index].param[i].flags & RPC_OUT) {
10570          tid = rpc_list[index].param[i].tid;
10571          flags = rpc_list[index].param[i].flags;
10572          param_size = ALIGN8(tid_size[tid]);
10573 
10574          if (tid == TID_STRING) {
10575             max_size = *((INT *) out_param_ptr);
10576             param_size = strlen((char *) prpc_param[i]) + 1;
10577             param_size = ALIGN8(param_size);
10578 
10579             /* move string ALIGN8(sizeof(INT)) left */
10580             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
10581 
10582             /* move remaining parameters to end of string */
10583             memcpy(out_param_ptr + param_size,
10584                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
10585                    (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
10586          }
10587 
10588          if (flags & RPC_VARARRAY) {
10589             /* store array length at current out_param_ptr */
10590             max_size = *((INT *) out_param_ptr);
10591             param_size = *((INT *) prpc_param[i + 1]);
10592             *((INT *) out_param_ptr) = param_size; // store new array size
10593             if (convert_flags)
10594                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
10595 
10596             out_param_ptr += ALIGN8(sizeof(INT));  // step over array size
10597 
10598             param_size = ALIGN8(param_size);
10599 
10600             /* move remaining parameters to end of array */
10601             memcpy(out_param_ptr + param_size,
10602                    out_param_ptr + max_size,
10603                    (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
10604          }
10605 
10606          if (tid == TID_STRUCT)
10607             param_size = ALIGN8(rpc_list[index].param[i].n);
10608 
10609          /* convert data format */
10610          if (convert_flags) {
10611             if (flags & RPC_VARARRAY)
10612                rpc_convert_data(out_param_ptr, tid,
10613                                 rpc_list[index].param[i].flags | RPC_OUTGOING, param_size, convert_flags);
10614             else
10615                rpc_convert_data(out_param_ptr, tid,
10616                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
10617                                 rpc_list[index].param[i].n * tid_size[tid], convert_flags);
10618          }
10619 
10620          out_param_ptr += param_size;
10621       }
10622 
10623    /* send return parameters */
10624    param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
10625    nc_out->header.routine_id = status;
10626    nc_out->header.param_size = param_size;
10627 
10628    /* convert header format (byte swapping) if necessary */
10629    if (convert_flags) {
10630       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
10631       rpc_convert_single(&nc_out->header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
10632    }
10633 
10634    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
10635 
10636    if (status < 0) {
10637       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
10638       M_FREE(return_buffer);
10639       return RPC_NET_ERROR;
10640    }
10641 
10642    /* print return buffer */
10643 /*
10644   printf("Return buffer, ID %d:\n", routine_id);
10645   for (i=0; i<param_size ; i++)
10646     {
10647     status = (char) nc_out->param[i];
10648     printf("%02X ", status);
10649     if (i%8 == 7)
10650       printf("\n");
10651     }
10652 */
10653    /* return SS_EXIT if RPC_EXIT is called */
10654    if (routine_id == RPC_ID_EXIT) {
10655       M_FREE(return_buffer);
10656       return SS_EXIT;
10657    }
10658 
10659    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
10660    if (routine_id == RPC_ID_SHUTDOWN) {
10661       M_FREE(return_buffer);
10662       return RPC_SHUTDOWN;
10663    }
10664 
10665    M_FREE(return_buffer);
10666 
10667    return RPC_SUCCESS;
10668 }
10669 
10670 
10671 /********************************************************************/
10672 INT rpc_execute_ascii(INT sock, char *buffer)
10673 /********************************************************************\
10674 
10675   Routine: rpc_execute_ascii
10676 
10677   Purpose: Execute a RPC command received over the network in ASCII
10678            mode
10679 
10680   Input:
10681     INT  sock               TCP socket to which the result should be
10682                             send back
10683 
10684     char *buffer            Command buffer
10685 
10686   Output:
10687     none
10688 
10689   Function value:
10690     RPC_SUCCESS             Successful completion
10691     RPC_INVALID_ID          Invalid routine_id received
10692     RPC_NET_ERROR           Error in socket call
10693     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10694     RPC_SHUTDOWN            Shutdown requested
10695     SS_ABORT                TCP connection broken
10696     SS_EXIT                 TCP connection closed
10697 
10698 \********************************************************************/
10699 {
10700 #define ASCII_BUFFER_SIZE 64500
10701 #define N_APARAM           1024
10702 
10703    INT i, j, index, status, index_in;
10704    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10705    INT routine_id, tid, flags, array_tid, n_param;
10706    INT param_size, item_size, num_values;
10707    void *prpc_param[20];
10708    char *arpc_param[N_APARAM], *pc;
10709    char str[1024], debug_line[1024];
10710    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
10711    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
10712    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
10713 
10714    /* parse arguments */
10715    arpc_param[0] = buffer;
10716    for (i = 1; i < N_APARAM; i++) {
10717       arpc_param[i] = strchr(arpc_param[i - 1], '&');
10718       if (arpc_param[i] == NULL)
10719          break;
10720       *arpc_param[i] = 0;
10721       arpc_param[i]++;
10722    }
10723 
10724    /* decode '%' */
10725    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
10726       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
10727          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
10728             str[0] = pc[1];
10729             str[1] = pc[2];
10730             str[2] = 0;
10731             sscanf(str, "%02X", &i);
10732 
10733             *pc++ = i;
10734             while (pc[2]) {
10735                pc[0] = pc[2];
10736                pc++;
10737             }
10738          }
10739       }
10740 
10741    /* find entry in rpc_list */
10742    for (i = 0;; i++)
10743       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
10744          break;
10745    index = i;
10746    routine_id = rpc_list[i].id;
10747    if (rpc_list[i].id == 0) {
10748       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
10749       return RPC_INVALID_ID;
10750    }
10751 
10752    in_param_ptr = buffer1;
10753    out_param_ptr = buffer2;
10754    index_in = 1;
10755 
10756    sprintf(debug_line, "%s(", rpc_list[index].name);
10757 
10758    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10759       tid = rpc_list[index].param[i].tid;
10760       flags = rpc_list[index].param[i].flags;
10761 
10762       if (flags & RPC_IN) {
10763          if (flags & RPC_VARARRAY) {
10764             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
10765 
10766             prpc_param[i] = in_param_ptr;
10767             for (j = 0; j < n_param; j++) {
10768                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
10769                in_param_ptr += param_size;
10770             }
10771             in_param_ptr = (char *) ALIGN8(((POINTER_T) in_param_ptr));
10772 
10773             strcat(debug_line, "<array>");
10774          } else {
10775             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
10776             param_size = ALIGN8(param_size);
10777 
10778             if (tid == TID_STRING || tid == TID_LINK)
10779                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10780 
10781             /*
10782                if (tid == TID_STRUCT)
10783                param_size = ALIGN8( rpc_list[index].param[i].n );
10784              */
10785             prpc_param[i] = in_param_ptr;
10786 
10787             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
10788             if (rpc_list[index].param[i].tid == TID_STRING) {
10789                /* check for long strings (db_create_record...) */
10790                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10791                   strcat(debug_line, "\"");
10792                   strcat(debug_line, str);
10793                   strcat(debug_line, "\"");
10794                } else
10795                   strcat(debug_line, "...");
10796             } else
10797                strcat(debug_line, str);
10798 
10799             in_param_ptr += param_size;
10800          }
10801 
10802          if ((POINTER_T) in_param_ptr - (POINTER_T) buffer1 > ASCII_BUFFER_SIZE) {
10803             cm_msg(MERROR, "rpc_ascii_execute",
10804                    "parameters (%d) too large for network buffer (%d)", param_size, ASCII_BUFFER_SIZE);
10805             return RPC_EXCEED_BUFFER;
10806          }
10807 
10808       }
10809 
10810       if (flags & RPC_OUT) {
10811          param_size = ALIGN8(tid_size[tid]);
10812 
10813          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10814             /* reserve maximum array length */
10815             param_size = atoi(arpc_param[index_in]);
10816             param_size = ALIGN8(param_size);
10817          }
10818 
10819 /*
10820       if (rpc_list[index].param[i].tid == TID_STRUCT)
10821         param_size = ALIGN8( rpc_list[index].param[i].n );
10822 */
10823          if ((POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size > ASCII_BUFFER_SIZE) {
10824             cm_msg(MERROR, "rpc_execute",
10825                    "return parameters (%d) too large for network buffer (%d)",
10826                    (POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size, ASCII_BUFFER_SIZE);
10827             return RPC_EXCEED_BUFFER;
10828          }
10829 
10830          /* if parameter goes both directions, copy input to output */
10831          if (rpc_list[index].param[i].flags & RPC_IN)
10832             memcpy(out_param_ptr, prpc_param[i], param_size);
10833 
10834          if (!(flags & RPC_IN))
10835             strcat(debug_line, "-");
10836 
10837          prpc_param[i] = out_param_ptr;
10838          out_param_ptr += param_size;
10839       }
10840 
10841       if (rpc_list[index].param[i + 1].tid)
10842          strcat(debug_line, ", ");
10843    }
10844 
10845    strcat(debug_line, ")");
10846    rpc_debug_printf(debug_line);
10847 
10848    last_param_ptr = out_param_ptr;
10849 
10850    /*********************************\
10851    *   call dispatch function        *
10852    \*********************************/
10853 
10854    if (rpc_list[index].dispatch)
10855       status = rpc_list[index].dispatch(routine_id, prpc_param);
10856    else
10857       status = RPC_INVALID_ID;
10858 
10859    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
10860       status = RPC_SUCCESS;
10861 
10862    /* Return if TCP connection broken */
10863    if (status == SS_ABORT)
10864       return SS_ABORT;
10865 
10866    /* if sock == 0, we are in FTCP mode and may not sent results */
10867    if (!sock)
10868       return RPC_SUCCESS;
10869 
10870    /* send return status */
10871    out_param_ptr = return_buffer;
10872    sprintf(out_param_ptr, "%d", status);
10873    out_param_ptr += strlen(out_param_ptr);
10874 
10875    /* convert return parameters */
10876    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10877       if (rpc_list[index].param[i].flags & RPC_OUT) {
10878          *out_param_ptr++ = '&';
10879 
10880          tid = rpc_list[index].param[i].tid;
10881          flags = rpc_list[index].param[i].flags;
10882          param_size = ALIGN8(tid_size[tid]);
10883 
10884          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
10885             strcpy(out_param_ptr, (char *) prpc_param[i]);
10886             param_size = strlen((char *) prpc_param[i]);
10887          }
10888 
10889          else if (flags & RPC_VARARRAY) {
10890             if (rpc_list[index].id == RPC_BM_RECEIVE_EVENT) {
10891                param_size = *((INT *) prpc_param[i + 1]);
10892                /* write number of bytes to output */
10893                sprintf(out_param_ptr, "%d", param_size);
10894                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
10895                memcpy(out_param_ptr, prpc_param[i], param_size);
10896                out_param_ptr += param_size;
10897                *out_param_ptr = 0;
10898             } else {
10899                if (rpc_list[index].id == RPC_DB_GET_DATA1) {
10900                   param_size = *((INT *) prpc_param[i + 1]);
10901                   array_tid = *((INT *) prpc_param[i + 2]);
10902                   num_values = *((INT *) prpc_param[i + 3]);
10903                } else if (rpc_list[index].id == RPC_DB_GET_DATA_INDEX) {
10904                   param_size = *((INT *) prpc_param[i + 1]);
10905                   array_tid = *((INT *) prpc_param[i + 3]);
10906                   num_values = 1;
10907                } else if (rpc_list[index].id == RPC_HS_READ) {
10908                   param_size = *((INT *) prpc_param[i + 1]);
10909                   if (i == 6) {
10910                      array_tid = TID_DWORD;
10911                      num_values = param_size / sizeof(DWORD);
10912                   } else {
10913                      array_tid = *((INT *) prpc_param[10]);
10914                      num_values = *((INT *) prpc_param[11]);
10915                   }
10916                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
10917 
10918                   param_size = *((INT *) prpc_param[i + 1]);
10919                   array_tid = tid;
10920                   if (tid == TID_STRING)
10921                      num_values = param_size / NAME_LENGTH;
10922                   else
10923                      num_values = param_size / tid_size[tid];
10924                }
10925 
10926                /* derive size of individual item */
10927                if (array_tid == TID_STRING)
10928                   item_size = param_size / num_values;
10929                else
10930                   item_size = tid_size[array_tid];
10931 
10932                /* write number of elements to output */
10933                sprintf(out_param_ptr, "%d", num_values);
10934                out_param_ptr += strlen(out_param_ptr);
10935 
10936                /* write array of values to output */
10937                for (j = 0; j < num_values; j++) {
10938                   *out_param_ptr++ = '&';
10939                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
10940                   out_param_ptr += strlen(out_param_ptr);
10941                }
10942             }
10943          }
10944 
10945 /*
10946       else if (tid == TID_STRUCT)
10947         param_size = ALIGN8( rpc_list[index].param[i].n );
10948 */
10949          else
10950             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
10951 
10952          out_param_ptr += strlen(out_param_ptr);
10953 
10954          if ((POINTER_T) out_param_ptr - (POINTER_T) return_buffer > ASCII_BUFFER_SIZE) {
10955             cm_msg(MERROR, "rpc_execute",
10956                    "return parameter (%d) too large for network buffer (%d)", param_size, ASCII_BUFFER_SIZE);
10957             return RPC_EXCEED_BUFFER;
10958          }
10959       }
10960 
10961    /* send return parameters */
10962    param_size = (POINTER_T) out_param_ptr - (POINTER_T) return_buffer + 1;
10963 
10964    status = send_tcp(sock, return_buffer, param_size, 0);
10965 
10966    if (status < 0) {
10967       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
10968       return RPC_NET_ERROR;
10969    }
10970 
10971    /* print return buffer */
10972    if (strlen(return_buffer) > sizeof(debug_line)) {
10973       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
10974       strcat(debug_line, "...");
10975    } else
10976       sprintf(debug_line, "-> %s", return_buffer);
10977    rpc_debug_printf(debug_line);
10978 
10979    /* return SS_EXIT if RPC_EXIT is called */
10980    if (routine_id == RPC_ID_EXIT)
10981       return SS_EXIT;
10982 
10983    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
10984    if (routine_id == RPC_ID_SHUTDOWN)
10985       return RPC_SHUTDOWN;
10986 
10987    return RPC_SUCCESS;
10988 }
10989 
10990 
10991 /********************************************************************/
10992 INT rpc_server_accept(int lsock)
10993 /********************************************************************\
10994 
10995   Routine: rpc_server_accept
10996 
10997   Purpose: Accept new incoming connections
10998 
10999   Input:
11000     INT    lscok            Listen socket
11001 
11002   Output:
11003     none
11004 
11005   Function value:
11006     RPC_SUCCESS             Successful completion
11007     RPC_NET_ERROR           Error in socket call
11008     RPC_CONNCLOSED          Connection was closed
11009     RPC_SHUTDOWN            Listener shutdown
11010     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11011 
11012 \********************************************************************/
11013 {
11014    INT index, i;
11015    unsigned int size;
11016    int status;
11017    char command, version[32], v1[32];
11018    INT sock, port1, port2, port3;
11019    struct sockaddr_in acc_addr;
11020    struct hostent *phe;
11021    char str[100];
11022    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11023    char debug_str[30];
11024    char *argv[10];
11025    char net_buffer[256];
11026    struct linger ling;
11027 
11028    static struct callback_addr callback;
11029 
11030    if (lsock > 0) {
11031       size = sizeof(acc_addr);
11032       sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11033 
11034       if (sock == -1)
11035          return RPC_NET_ERROR;
11036    } else {
11037       /* lsock is stdin -> already connected from inetd */
11038 
11039       size = sizeof(acc_addr);
11040       sock = lsock;
11041       getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11042    }
11043 
11044    /* receive string with timeout */
11045    i = recv_string(sock, net_buffer, 256, 10000);
11046    rpc_debug_printf("Received command: %s", net_buffer);
11047 
11048    if (i > 0) {
11049       command = (char) toupper(net_buffer[0]);
11050 
11051       switch (command) {
11052       case 'S':
11053 
11054          /*----------- shutdown listener ----------------------*/
11055          closesocket(sock);
11056          return RPC_SHUTDOWN;
11057 
11058       case 7:
11059          ss_shell(sock);
11060          closesocket(sock);
11061          break;
11062 
11063       case 'I':
11064 
11065          /*----------- return available experiments -----------*/
11066          cm_scan_experiments();
11067          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11068             rpc_debug_printf("Return experiment: %s", exptab[i].name);
11069             sprintf(str, "%s", exptab[i].name);
11070             send(sock, str, strlen(str) + 1, 0);
11071          }
11072          send(sock, "", 1, 0);
11073          closesocket(sock);
11074          break;
11075 
11076       case 'C':
11077 
11078          /*----------- connect to experiment -----------*/
11079 
11080          /* get callback information */
11081          callback.experiment[0] = 0;
11082          port1 = port2 = version[0] = 0;
11083          sscanf(net_buffer + 2, "%d %d %d %s", &port1, &port2, &port3, version);
11084          strcpy(callback.experiment,
11085                 strchr(strchr(strchr(strchr(net_buffer + 2, ' ') + 1, ' ') + 1, ' ') + 1, ' ') + 1);
11086 
11087          /* print warning if version patch level doesn't agree */
11088          strcpy(v1, version);
11089          if (strchr(v1, '.'))
11090             if (strchr(strchr(v1, '.') + 1, '.'))
11091                *strchr(strchr(v1, '.') + 1, '.') = 0;
11092 
11093          strcpy(str, cm_get_version());
11094          if (strchr(str, '.'))
11095             if (strchr(strchr(str, '.') + 1, '.'))
11096                *strchr(strchr(str, '.') + 1, '.') = 0;
11097 
11098          if (strcmp(v1, str) != 0) {
11099             sprintf(str, "client MIDAS version %s differs from local version %s", version, cm_get_version());
11100             cm_msg(MERROR, "rpc_server_accept", str);
11101 
11102             sprintf(str, "received string: %s", net_buffer + 2);
11103             cm_msg(MERROR, "rpc_server_accept", str);
11104          }
11105 
11106          callback.host_port1 = (short) port1;
11107          callback.host_port2 = (short) port2;
11108          callback.host_port3 = (short) port3;
11109          callback.debug = _debug_mode;
11110 
11111          /* get the name of the remote host */
11112 #ifdef OS_VXWORKS
11113          {
11114             INT status;
11115             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11116             if (status != 0) {
11117                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11118                break;
11119             }
11120          }
11121 #else
11122          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11123          if (phe == NULL) {
11124             /* use IP number instead */
11125             strcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr));
11126          } else
11127             strcpy(callback.host_name, phe->h_name);
11128 #endif
11129 
11130          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11131             /* update experiment definition */
11132             cm_scan_experiments();
11133 
11134             /* lookup experiment */
11135             if (equal_ustring(callback.experiment, "Default"))
11136                index = 0;
11137             else
11138                for (index = 0; index < MAX_EXPERIMENT && exptab[index].name[0]; index++)
11139                   if (equal_ustring(callback.experiment, exptab[index].name))
11140                      break;
11141 
11142             if (index == MAX_EXPERIMENT || exptab[index].name[0] == 0) {
11143                sprintf(str, "experiment %s not defined in exptab\r", callback.experiment);
11144                cm_msg(MERROR, "rpc_server_accept", str);
11145 
11146                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11147                closesocket(sock);
11148                break;
11149             }
11150 
11151             strcpy(callback.directory, exptab[index].directory);
11152             strcpy(callback.user, exptab[index].user);
11153 
11154             /* create a new process */
11155             sprintf(host_port1_str, "%d", callback.host_port1);
11156             sprintf(host_port2_str, "%d", callback.host_port2);
11157             sprintf(host_port3_str, "%d", callback.host_port3);
11158             sprintf(debug_str, "%d", callback.debug);
11159 
11160             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11161             argv[1] = callback.host_name;
11162             argv[2] = host_port1_str;
11163             argv[3] = host_port2_str;
11164             argv[4] = host_port3_str;
11165             argv[5] = debug_str;
11166             argv[6] = callback.experiment;
11167             argv[7] = callback.directory;
11168             argv[8] = callback.user;
11169             argv[9] = NULL;
11170 
11171             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
11172                              argv[0], argv[1], argv[2], argv[3], argv[4],
11173                              argv[5], argv[6], argv[7], argv[8], argv[9]);
11174 
11175             status = ss_spawnv(P_NOWAIT, (char *) rpc_get_server_option(RPC_OSERVER_NAME), argv);
11176 
11177             if (status != SS_SUCCESS) {
11178                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
11179 
11180                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
11181                send(sock, str, strlen(str) + 1, 0);
11182                closesocket(sock);
11183                break;
11184             }
11185 
11186             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11187             send(sock, str, strlen(str) + 1, 0);
11188             closesocket(sock);
11189          } else {
11190             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11191             send(sock, str, strlen(str) + 1, 0);
11192             closesocket(sock);
11193          }
11194 
11195          /* look for next free entry */
11196          for (index = 0; index < MAX_RPC_CONNECTION; index++)
11197             if (_server_acception[index].recv_sock == 0)
11198                break;
11199          if (index == MAX_RPC_CONNECTION)
11200             return RPC_NET_ERROR;
11201          callback.index = index;
11202 
11203         /*----- multi thread server ------------------------*/
11204          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11205             ss_thread_create(rpc_server_thread, (void *) (&callback));
11206 
11207         /*----- single thread server -----------------------*/
11208          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
11209              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11210             rpc_server_callback(&callback);
11211 
11212          break;
11213 
11214       default:
11215          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c'", command);
11216          closesocket(sock);
11217          break;
11218 
11219       }
11220    } else {                     /* if i>0 */
11221 
11222       /* lingering needed for PCTCP */
11223       ling.l_onoff = 1;
11224       ling.l_linger = 0;
11225       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11226       closesocket(sock);
11227    }
11228 
11229    return RPC_SUCCESS;
11230 }
11231 
11232 /********************************************************************/
11233 INT rpc_client_accept(int lsock)
11234 /********************************************************************\
11235 
11236   Routine: rpc_client_accept
11237 
11238   Purpose: Accept new incoming connections as a client
11239 
11240   Input:
11241     INT    lsock            Listen socket
11242 
11243   Output:
11244     none
11245 
11246   Function value:
11247     RPC_SUCCESS             Successful completion
11248     RPC_NET_ERROR           Error in socket call
11249     RPC_CONNCLOSED          Connection was closed
11250     RPC_SHUTDOWN            Listener shutdown
11251     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11252 
11253 \********************************************************************/
11254 {
11255    INT index, i, version, status;
11256    unsigned int size;
11257    int sock;
11258    struct sockaddr_in acc_addr;
11259    INT client_hw_type = 0, hw_type;
11260    char str[100], client_program[NAME_LENGTH];
11261    char host_name[HOST_NAME_LENGTH];
11262    INT convert_flags;
11263    char net_buffer[256], *p;
11264 
11265    size = sizeof(acc_addr);
11266    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11267 
11268    if (sock == -1)
11269       return RPC_NET_ERROR;
11270 
11271    /* get the name of the calling host */
11272 /* outcommented for speed reasons SR 7.10.98
11273 #ifdef OS_VXWORKS
11274   {
11275   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
11276   if (status != 0)
11277     strcpy(host_name, "unknown");
11278   }
11279 #else
11280   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11281   if (phe == NULL)
11282     strcpy(host_name, "unknown");
11283   strcpy(host_name, phe->h_name);
11284 #endif
11285 */
11286    strcpy(host_name, "");
11287 
11288    /* look for next free entry */
11289    for (index = 0; index < MAX_RPC_CONNECTION; index++)
11290       if (_server_acception[index].recv_sock == 0)
11291          break;
11292    if (index == MAX_RPC_CONNECTION) {
11293       closesocket(sock);
11294       return RPC_NET_ERROR;
11295    }
11296 
11297    /* receive string with timeout */
11298    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
11299    if (i <= 0) {
11300       closesocket(sock);
11301       return RPC_NET_ERROR;
11302    }
11303 
11304    /* get remote computer info */
11305    p = strtok(net_buffer, " ");
11306    if (p != NULL) {
11307       client_hw_type = atoi(p);
11308       p = strtok(NULL, " ");
11309    }
11310    if (p != NULL) {
11311       version = atoi(p);
11312       p = strtok(NULL, " ");
11313    }
11314    if (p != NULL) {
11315       strcpy(client_program, p);
11316       p = strtok(NULL, " ");
11317    }
11318    if (p != NULL) {
11319       strcpy(host_name, p);
11320       p = strtok(NULL, " ");
11321    }
11322 
11323    /* save information in _server_acception structure */
11324    _server_acception[index].recv_sock = sock;
11325    _server_acception[index].send_sock = 0;
11326    _server_acception[index].event_sock = 0;
11327    _server_acception[index].remote_hw_type = client_hw_type;
11328    strcpy(_server_acception[index].host_name, host_name);
11329    strcpy(_server_acception[index].prog_name, client_program);
11330    _server_acception[index].tid = ss_gettid();
11331    _server_acception[index].last_activity = ss_millitime();
11332    _server_acception[index].watchdog_timeout = 0;
11333 
11334    /* send my own computer id */
11335    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11336    sprintf(str, "%d %s", hw_type, cm_get_version());
11337    status = send(sock, str, strlen(str) + 1, 0);
11338    if (status != (INT) strlen(str) + 1)
11339       return RPC_NET_ERROR;
11340 
11341    rpc_set_server_acception(index + 1);
11342    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11343    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11344 
11345    /* set callback function for ss_suspend */
11346    ss_suspend_set_dispatch(CH_SERVER, _server_acception, (int (*)(void)) rpc_server_receive);
11347 
11348    return RPC_SUCCESS;
11349 }
11350 
11351 /********************************************************************/
11352 INT rpc_server_callback(struct callback_addr * pcallback)
11353 /********************************************************************\
11354 
11355   Routine: rpc_server_callback
11356 
11357   Purpose: Callback a remote client. Setup _server_acception entry
11358            with optional conversion flags and establish two-way
11359            TCP connection.
11360 
11361   Input:
11362     callback_addr pcallback Pointer to a callback structure
11363 
11364   Output:
11365     none
11366 
11367   Function value:
11368     RPC_SUCCESS             Successful completion
11369 
11370 \********************************************************************/
11371 {
11372    INT index, status;
11373    int recv_sock, send_sock, event_sock;
11374    struct sockaddr_in bind_addr;
11375    struct hostent *phe;
11376    char str[100], client_program[NAME_LENGTH];
11377    char host_name[HOST_NAME_LENGTH];
11378    INT client_hw_type, hw_type;
11379    INT convert_flags;
11380    char net_buffer[256];
11381    struct callback_addr callback;
11382    int flag;
11383 
11384    /* copy callback information */
11385    memcpy(&callback, pcallback, sizeof(callback));
11386    index = callback.index;
11387 
11388    /* create new sockets for TCP */
11389    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
11390    send_sock = socket(AF_INET, SOCK_STREAM, 0);
11391    event_sock = socket(AF_INET, SOCK_STREAM, 0);
11392    if (event_sock == -1)
11393       return RPC_NET_ERROR;
11394 
11395    /* callback to remote node */
11396    memset(&bind_addr, 0, sizeof(bind_addr));
11397    bind_addr.sin_family = AF_INET;
11398    bind_addr.sin_port = htons(callback.host_port1);
11399 
11400 #ifdef OS_VXWORKS
11401    {
11402       INT host_addr;
11403 
11404       host_addr = hostGetByName(callback.host_name);
11405       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
11406    }
11407 #else
11408    phe = gethostbyname(callback.host_name);
11409    if (phe == NULL) {
11410       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
11411       return RPC_NET_ERROR;
11412    }
11413    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
11414 #endif
11415 
11416    /* connect receive socket */
11417 #ifdef OS_UNIX
11418    do {
11419       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
11420 
11421       /* don't return if an alarm signal was cought */
11422    } while (status == -1 && errno == EINTR);
11423 #else
11424    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11425 #endif
11426 
11427    if (status != 0) {
11428       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
11429       goto error;
11430    }
11431 
11432    bind_addr.sin_port = htons(callback.host_port2);
11433 
11434    /* connect send socket */
11435 #ifdef OS_UNIX
11436    do {
11437       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11438 
11439       /* don't return if an alarm signal was cought */
11440    } while (status == -1 && errno == EINTR);
11441 #else
11442    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11443 #endif
11444 
11445    if (status != 0) {
11446       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
11447       goto error;
11448    }
11449 
11450    bind_addr.sin_port = htons(callback.host_port3);
11451 
11452    /* connect event socket */
11453 #ifdef OS_UNIX
11454    do {
11455       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11456 
11457       /* don't return if an alarm signal was cought */
11458    } while (status == -1 && errno == EINTR);
11459 #else
11460    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11461 #endif
11462 
11463    if (status != 0) {
11464       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
11465       goto error;
11466    }
11467 
11468    /* increase receive buffer size to 64k */
11469 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
11470    flag = 0x10000;
11471    setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
11472 #endif
11473 
11474    if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
11475       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
11476       goto error;
11477    }
11478 
11479    /* get remote computer info */
11480    sscanf(net_buffer, "%d", &client_hw_type);
11481 
11482    strcpy(client_program, strchr(net_buffer, ' ') + 1);
11483 
11484    /* get the name of the remote host */
11485 #ifdef OS_VXWORKS
11486    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
11487    if (status != 0)
11488       strcpy(host_name, "unknown");
11489 #else
11490    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
11491    if (phe == NULL)
11492       strcpy(host_name, "unknown");
11493    else
11494       strcpy(host_name, phe->h_name);
11495 #endif
11496 
11497    /* save information in _server_acception structure */
11498    _server_acception[index].recv_sock = recv_sock;
11499    _server_acception[index].send_sock = send_sock;
11500    _server_acception[index].event_sock = event_sock;
11501    _server_acception[index].remote_hw_type = client_hw_type;
11502    strcpy(_server_acception[index].host_name, host_name);
11503    strcpy(_server_acception[index].prog_name, client_program);
11504    _server_acception[index].tid = ss_gettid();
11505    _server_acception[index].last_activity = ss_millitime();
11506    _server_acception[index].watchdog_timeout = 0;
11507 
11508    /* send my own computer id */
11509    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11510    sprintf(str, "%d", hw_type);
11511    send(recv_sock, str, strlen(str) + 1, 0);
11512 
11513    rpc_set_server_acception(index + 1);
11514    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11515    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11516 
11517    /* set callback function for ss_suspend */
11518    ss_suspend_set_dispatch(CH_SERVER, _server_acception, (int (*)(void)) rpc_server_receive);
11519 
11520    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11521       rpc_debug_printf("Connection to %s:%s established\n",
11522                        _server_acception[index].host_name, _server_acception[index].prog_name);
11523 
11524    return RPC_SUCCESS;
11525 
11526  error:
11527 
11528    closesocket(recv_sock);
11529    closesocket(send_sock);
11530    closesocket(event_sock);
11531 
11532    return RPC_NET_ERROR;
11533 }
11534 
11535 
11536 /********************************************************************/
11537 INT rpc_server_thread(void *pointer)
11538 /********************************************************************\
11539 
11540   Routine: rpc_server_thread
11541 
11542   Purpose: New thread for a multi-threaded server. Callback to the
11543            client and process RPC requests.
11544 
11545   Input:
11546     vcoid  pointer          pointer to callback_addr structure.
11547 
11548   Output:
11549     none
11550 
11551   Function value:
11552     RPC_SUCCESS             Successful completion
11553 
11554 \********************************************************************/
11555 {
11556    struct callback_addr callback;
11557    int status, mutex_alarm, mutex_elog;
11558    static DWORD last_checked = 0;
11559 
11560    memcpy(&callback, pointer, sizeof(callback));
11561 
11562    status = rpc_server_callback(&callback);
11563 
11564    if (status != RPC_SUCCESS)
11565       return status;
11566 
11567    /* create alarm and elog mutexes */
11568    ss_mutex_create("ALARM", &mutex_alarm);
11569    ss_mutex_create("ELOG", &mutex_elog);
11570    cm_set_experiment_mutex(mutex_alarm, mutex_elog);
11571 
11572    do {
11573       status = ss_suspend(5000, 0);
11574 
11575       if (rpc_check_channels() == RPC_NET_ERROR)
11576          break;
11577 
11578       /* check alarms every 10 seconds */
11579       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
11580          al_check();
11581          last_checked = ss_time();
11582       }
11583 
11584    } while (status != SS_ABORT && status != SS_EXIT);
11585 
11586    /* delete entry in suspend table for this thread */
11587    ss_suspend_exit();
11588 
11589    return RPC_SUCCESS;
11590 }
11591 
11592 
11593 /********************************************************************/
11594 INT rpc_server_receive(INT index, int sock, BOOL check)
11595 /********************************************************************\
11596 
11597   Routine: rpc_server_receive
11598 
11599   Purpose: Receive rpc commands and execute them. Close the connection
11600            if client has broken TCP pipe.
11601 
11602   Input:
11603     INT    index            Index to _server_acception structure in-
11604                             dicating which connection got data.
11605     int    sock             Socket which got data
11606     BOOL   check            If TRUE, only check if connection is
11607                             broken. This may be called via
11608                             bm_receive_event/ss_suspend(..,MSG_BM)
11609 
11610   Output:
11611     none
11612 
11613   Function value:
11614     RPC_SUCCESS             Successful completion
11615     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
11616     SS_EXIT                 Server connection was closed
11617     SS_ABORT                Server connection was broken
11618 
11619 \********************************************************************/
11620 {
11621    INT status, n_received;
11622    INT remaining, *pbh, start_time;
11623    char test_buffer[256], str[80];
11624    EVENT_HEADER *pevent;
11625 
11626    /* init network buffer */
11627    if (_net_recv_buffer_size == 0) {
11628       _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
11629       if (_net_recv_buffer == NULL) {
11630          cm_msg(MERROR, "rpc_server_receive", "not enough memory to allocate network buffer");
11631          return RPC_EXCEED_BUFFER;
11632       }
11633       _net_recv_buffer_size = NET_BUFFER_SIZE;
11634    }
11635 
11636    /* only check if TCP connection is broken */
11637    if (check) {
11638       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
11639 
11640       if (n_received == -1)
11641          cm_msg(MERROR, "rpc_server_receive",
11642                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
11643                 n_received, errno, strerror(errno));
11644 
11645       if (n_received <= 0)
11646          return SS_ABORT;
11647 
11648       return SS_SUCCESS;
11649    }
11650 
11651    remaining = 0;
11652 
11653    /* receive command */
11654    if (sock == _server_acception[index].recv_sock) {
11655       do {
11656          if (_server_acception[index].remote_hw_type == DR_ASCII)
11657             n_received = recv_string(_server_acception[index].recv_sock, _net_recv_buffer,
11658                                      NET_BUFFER_SIZE, 10000);
11659          else
11660             n_received = recv_tcp_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
11661 
11662          if (n_received <= 0) {
11663             status = SS_ABORT;
11664             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort", n_received);
11665             goto error;
11666          }
11667 
11668          rpc_set_server_acception(index + 1);
11669 
11670          if (_server_acception[index].remote_hw_type == DR_ASCII)
11671             status = rpc_execute_ascii(_server_acception[index].recv_sock, _net_recv_buffer);
11672          else
11673             status = rpc_execute(_server_acception[index].recv_sock,
11674                                  _net_recv_buffer, _server_acception[index].convert_flags);
11675 
11676          if (status == SS_ABORT) {
11677             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort", status);
11678             goto error;
11679          }
11680 
11681          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
11682             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11683                rpc_debug_printf("Connection to %s:%s closed\n",
11684                                 _server_acception[index].host_name, _server_acception[index].prog_name);
11685             goto exit;
11686          }
11687 
11688       } while (remaining);
11689    } else {
11690       /* receive event */
11691       if (sock == _server_acception[index].event_sock) {
11692          start_time = ss_millitime();
11693 
11694          do {
11695             n_received = recv_event_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
11696 
11697             if (n_received <= 0) {
11698                status = SS_ABORT;
11699                cm_msg(MERROR, "rpc_server_receive", "recv_event_server() returned %d, abort", n_received);
11700                goto error;
11701             }
11702 
11703             /* send event to buffer */
11704             pbh = (INT *) _net_recv_buffer;
11705             pevent = (EVENT_HEADER *) (pbh + 1);
11706 
11707             status = bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
11708             if (status != BM_SUCCESS)
11709                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d", status);
11710 
11711             /* repeat for maximum 0.5 sec */
11712          } while (ss_millitime() - start_time < 500 && remaining);
11713       }
11714    }
11715 
11716    return RPC_SUCCESS;
11717 
11718  error:
11719 
11720    strcpy(str, _server_acception[index].host_name);
11721    if (strchr(str, '.'))
11722       *strchr(str, '.') = 0;
11723    cm_msg(MTALK, "rpc_server_receive", "Program %s on host %s aborted",
11724           _server_acception[index].prog_name, str);
11725 
11726  exit:
11727 
11728    /* disconnect from experiment as MIDAS server */
11729    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
11730       HNDLE hDB, hKey;
11731 
11732       cm_get_experiment_database(&hDB, &hKey);
11733 
11734       /* only disconnect from experiment if previously connected.
11735          Necessary for pure RPC servers (RPC_SRVR) */
11736       if (hDB) {
11737 #ifdef LOCAL_ROUTINES
11738          ss_alarm(0, cm_watchdog);
11739 #endif
11740 
11741          cm_delete_client_info(hDB, 0);
11742 
11743          bm_close_all_buffers();
11744          db_close_all_databases();
11745 
11746          rpc_deregister_functions();
11747 
11748          cm_set_experiment_database(0, 0);
11749 
11750          _msg_buffer = 0;
11751       }
11752    }
11753 
11754    /* close server connection */
11755    if (_server_acception[index].recv_sock)
11756       closesocket(_server_acception[index].recv_sock);
11757    if (_server_acception[index].send_sock)
11758       closesocket(_server_acception[index].send_sock);
11759    if (_server_acception[index].event_sock)
11760       closesocket(_server_acception[index].event_sock);
11761 
11762    /* free TCP cache */
11763    M_FREE(_server_acception[index].net_buffer);
11764    _server_acception[index].net_buffer = NULL;
11765 
11766    /* mark this entry as invalid */
11767    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
11768 
11769    /* signal caller a shutdonw */
11770    if (status == RPC_SHUTDOWN)
11771       return status;
11772 
11773    /* don't abort if other than main connection is broken */
11774    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11775       return SS_SUCCESS;
11776 
11777    return status;
11778 }
11779 
11780 
11781 /********************************************************************/
11782 INT rpc_server_shutdown(void)
11783 /********************************************************************\
11784 
11785   Routine: rpc_server_shutdown
11786 
11787   Purpose: Shutdown RPC server, abort all connections
11788 
11789   Input:
11790     none
11791 
11792   Output:
11793     none
11794 
11795   Function value:
11796     RPC_SUCCESS             Successful completion
11797 
11798 \********************************************************************/
11799 {
11800    INT i;
11801    struct linger ling;
11802 
11803    /* close all open connections */
11804    for (i = 0; i < MAX_RPC_CONNECTION; i++)
11805       if (_server_acception[i].recv_sock != 0) {
11806          /* lingering needed for PCTCP */
11807          ling.l_onoff = 1;
11808          ling.l_linger = 0;
11809          setsockopt(_server_acception[i].recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11810          closesocket(_server_acception[i].recv_sock);
11811 
11812          if (_server_acception[i].send_sock) {
11813             setsockopt(_server_acception[i].send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11814             closesocket(_server_acception[i].send_sock);
11815          }
11816 
11817          if (_server_acception[i].event_sock) {
11818             setsockopt(_server_acception[i].event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11819             closesocket(_server_acception[i].event_sock);
11820          }
11821 
11822          _server_acception[i].recv_sock = 0;
11823          _server_acception[i].send_sock = 0;
11824          _server_acception[i].event_sock = 0;
11825       }
11826 
11827    if (_lsock) {
11828       closesocket(_lsock);
11829       _lsock = 0;
11830       _server_registered = FALSE;
11831    }
11832 
11833    /* free suspend structures */
11834    ss_suspend_exit();
11835 
11836    return RPC_SUCCESS;
11837 }
11838 
11839 
11840 /********************************************************************/
11841 INT rpc_check_channels(void)
11842 /********************************************************************\
11843 
11844   Routine: rpc_check_channels
11845 
11846   Purpose: Check open rpc channels by sending watchdog messages
11847 
11848   Input:
11849     none
11850 
11851   Output:
11852     none
11853 
11854   Function value:
11855     RPC_SUCCESS             Channel is still alive
11856     RPC_NET_ERROR           Connection is broken
11857 
11858 \********************************************************************/
11859 {
11860    INT status, index, i, convert_flags;
11861    NET_COMMAND nc;
11862    fd_set readfds;
11863    struct timeval timeout;
11864 
11865    for (index = 0; index < MAX_RPC_CONNECTION; index++) {
11866       if (_server_acception[index].recv_sock &&
11867           _server_acception[index].tid == ss_gettid() &&
11868           _server_acception[index].watchdog_timeout &&
11869           (ss_millitime() - _server_acception[index].last_activity >
11870            (DWORD) _server_acception[index].watchdog_timeout)) {
11871 /* printf("Send watchdog message to %s on %s\n",
11872                 _server_acception[index].prog_name,
11873                 _server_acception[index].host_name); */
11874 
11875          /* send a watchdog message */
11876          nc.header.routine_id = MSG_WATCHDOG;
11877          nc.header.param_size = 0;
11878 
11879          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
11880          if (convert_flags) {
11881             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
11882             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
11883          }
11884 
11885          /* send the header to the client */
11886          i = send_tcp(_server_acception[index].send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
11887 
11888          if (i < 0)
11889             goto exit;
11890 
11891          /* make some timeout checking */
11892          FD_ZERO(&readfds);
11893          FD_SET(_server_acception[index].send_sock, &readfds);
11894          FD_SET(_server_acception[index].recv_sock, &readfds);
11895 
11896          timeout.tv_sec = _server_acception[index].watchdog_timeout / 1000;
11897          timeout.tv_usec = (_server_acception[index].watchdog_timeout % 1000) * 1000;
11898 
11899          do {
11900             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
11901 
11902             /* if an alarm signal was cought, restart select with reduced timeout */
11903             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
11904                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
11905 
11906          } while (status == -1);        /* dont return if an alarm signal was cought */
11907 
11908          if (!FD_ISSET(_server_acception[index].send_sock, &readfds) &&
11909              !FD_ISSET(_server_acception[index].recv_sock, &readfds))
11910             goto exit;
11911 
11912          /* receive result on send socket */
11913          if (FD_ISSET(_server_acception[index].send_sock, &readfds)) {
11914             i = recv_tcp(_server_acception[index].send_sock, (char *) &nc, sizeof(nc), 0);
11915             if (i <= 0)
11916                goto exit;
11917          }
11918       }
11919    }
11920 
11921    return RPC_SUCCESS;
11922 
11923  exit:
11924 
11925    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
11926           _server_acception[index].host_name,
11927           _server_acception[index].prog_name, _server_acception[index].watchdog_timeout / 1000);
11928 
11929    /* disconnect from experiment */
11930    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11931       cm_disconnect_experiment();
11932 
11933    /* close server connection */
11934    if (_server_acception[index].recv_sock)
11935       closesocket(_server_acception[index].recv_sock);
11936    if (_server_acception[index].send_sock)
11937       closesocket(_server_acception[index].send_sock);
11938    if (_server_acception[index].event_sock)
11939       closesocket(_server_acception[index].event_sock);
11940 
11941    /* free TCP cache */
11942    M_FREE(_server_acception[index].net_buffer);
11943    _server_acception[index].net_buffer = NULL;
11944 
11945    /* mark this entry as invalid */
11946    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
11947 
11948    return RPC_NET_ERROR;
11949 }
11950 
11951 /**dox***************************************************************/
11952 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
11953 
11954 /**dox***************************************************************/
11955                                                                                                              /** @} *//* end of rpcfunctionc */
11956 
11957 /**dox***************************************************************/
11958 /** @addtogroup bkfunctionc
11959  *
11960  *  @{  */
11961 
11962 /********************************************************************\
11963 *                                                                    *
11964 *                 Bank functions                                     *
11965 *                                                                    *
11966 \********************************************************************/
11967 
11968 /********************************************************************/
11969 /**
11970 Initializes an event for Midas banks structure.
11971 Before banks can be created in an event, bk_init() has to be called first.
11972 @param event pointer to the area of event
11973 */
11974 void bk_init(void *event)
11975 {
11976    ((BANK_HEADER *) event)->data_size = 0;
11977    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
11978 }
11979 
11980 /**dox***************************************************************/
11981 #ifndef DOXYGEN_SHOULD_SKIP_THIS
11982 
11983 /********************************************************************/
11984 BOOL bk_is32(void *event)
11985 /********************************************************************\
11986 
11987   Routine: bk_is32
11988 
11989   Purpose: Return true if banks inside event are 32-bit banks
11990 
11991   Input:
11992     void   *event           pointer to the event
11993 
11994   Output:
11995     none
11996 
11997   Function value:
11998     none
11999 
12000 \********************************************************************/
12001 {
12002    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12003 }
12004 
12005 /**dox***************************************************************/
12006 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12007 
12008 /********************************************************************/
12009 /**
12010 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12011 Before banks can be created in an event, bk_init32() has to be called first.
12012 @param event pointer to the area of event
12013 @return void
12014 */
12015 void bk_init32(void *event)
12016 {
12017    ((BANK_HEADER *) event)->data_size = 0;
12018    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12019 }
12020 
12021 /********************************************************************/
12022 /**
12023 Returns the size of an event containing banks.
12024 The total size of an event is the value returned by bk_size() plus the size
12025 of the event header (sizeof(EVENT_HEADER)).
12026 @param event pointer to the area of event
12027 @return number of bytes contained in data area of event
12028 */
12029 INT bk_size(void *event)
12030 {
12031    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12032 }
12033 
12034 /********************************************************************/
12035 /**
12036 Create a Midas bank.
12037 The data pointer pdata must be used as an address to fill a
12038 bank. It is incremented with every value written to the bank and finally points
12039 to a location just after the last byte of the bank. It is then passed to
12040 the function bk_close() to finish the bank creation.
12041 \code
12042 INT *pdata;
12043 bk_init(pevent);
12044 bk_create(pevent, "ADC0", TID_INT, &pdata);
12045 *pdata++ = 123
12046 *pdata++ = 456
12047 bk_close(pevent, pdata);
12048 \endcode
12049 @param event pointer to the data area
12050 @param name of the bank, must be exactly 4 charaters
12051 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12052 midas.h
12053 @param pdata pointer to the data area of the newly created bank
12054 @return void
12055 */
12056 void bk_create(void *event, const char *name, WORD type, void *pdata)
12057 {
12058    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12059       BANK32 *pbk32;
12060 
12061       pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12062       strncpy(pbk32->name, name, 4);
12063       pbk32->type = type;
12064       pbk32->data_size = 0;
12065       *((void **) pdata) = pbk32 + 1;
12066    } else {
12067       BANK *pbk;
12068 
12069       pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12070       strncpy(pbk->name, name, 4);
12071       pbk->type = type;
12072       pbk->data_size = 0;
12073       *((void **) pdata) = pbk + 1;
12074    }
12075 }
12076 
12077 
12078 
12079 /**dox***************************************************************/
12080 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12081 
12082 /********************************************************************/
12083 int bk_delete(void *event, const char *name)
12084 /********************************************************************\
12085 
12086   Routine: bk_delete
12087 
12088   Purpose: Delete a MIDAS bank inside an event
12089 
12090   Input:
12091     void   *event           pointer to the event
12092     char   *name            Name of bank (exactly four letters)
12093 
12094   Function value:
12095     CM_SUCCESS              Bank has been deleted
12096     0                       Bank has not been found
12097 
12098 \********************************************************************/
12099 {
12100    BANK *pbk;
12101    BANK32 *pbk32;
12102    DWORD dname;
12103    int remaining;
12104 
12105    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12106       /* locate bank */
12107       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12108       strncpy((char *) &dname, name, 4);
12109       do {
12110          if (*((DWORD *) pbk32->name) == dname) {
12111             /* bank found, delete it */
12112             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
12113                          sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12114 
12115             /* reduce total event size */
12116             ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
12117 
12118             /* copy remaining bytes */
12119             if (remaining > 0)
12120                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12121             return CM_SUCCESS;
12122          }
12123 
12124          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12125       } while ((DWORD) ((char *) pbk32 - (char *) event) <
12126                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12127    } else {
12128       /* locate bank */
12129       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12130       strncpy((char *) &dname, name, 4);
12131       do {
12132          if (*((DWORD *) pbk->name) == dname) {
12133             /* bank found, delete it */
12134             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
12135                          sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12136 
12137             /* reduce total event size */
12138             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
12139 
12140             /* copy remaining bytes */
12141             if (remaining > 0)
12142                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
12143             return CM_SUCCESS;
12144          }
12145 
12146          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12147       } while ((DWORD) ((char *) pbk - (char *) event) <
12148                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12149    }
12150 
12151    return 0;
12152 }
12153 
12154 /**dox***************************************************************/
12155 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12156 
12157 /********************************************************************/
12158 /**
12159 Close the Midas bank priviously created by bk_create().
12160 The data pointer pdata must be obtained by bk_create() and
12161 used as an address to fill a bank. It is incremented with every value written
12162 to the bank and finally points to a location just after the last byte of the
12163 bank. It is then passed to bk_close() to finish the bank creation
12164 @param event pointer to current composed event
12165 @param pdata  pointer to the data
12166 @return number of bytes contained in bank
12167 */
12168 INT bk_close(void *event, void *pdata)
12169 {
12170    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12171       BANK32 *pbk32;
12172 
12173       pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12174       pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
12175       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12176          printf("Warning: bank %c%c%c%c has zero size\n",
12177                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
12178       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
12179       return pbk32->data_size;
12180    } else {
12181       BANK *pbk;
12182 
12183       pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12184       pbk->data_size = (WORD) ((char *) pdata - (char *) (pbk + 1));
12185       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12186          printf("Warning: bank %c%c%c%c has zero size\n",
12187                 pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12188       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
12189       return pbk->data_size;
12190    }
12191 }
12192 
12193 /********************************************************************/
12194 /**
12195 Extract the MIDAS bank name listing of an event.
12196 The bklist should be dimensioned with STRING_BANKLIST_MAX
12197 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12198 \code
12199 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12200 {
12201   INT    n_adc, nbanks;
12202   WORD   *pdata;
12203   char   banklist[STRING_BANKLIST_MAX];
12204 
12205   // Display # of banks and list of banks in the event
12206   nbanks = bk_list(pevent, banklist);
12207   printf("#banks:%d List:%s\n", nbanks, banklist);
12208 
12209   // look for ADC0 bank, return if not present
12210   n_adc = bk_locate(pevent, "ADC0", &pdata);
12211   ...
12212 }
12213 \endcode
12214 @param event pointer to current composed event
12215 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12216 @return number of bank found in this event.
12217 */
12218 INT bk_list(void *event, char *bklist)
12219 {                               /* Full event */
12220    INT nbk, size;
12221    BANK *pmbk = NULL;
12222    BANK32 *pmbk32 = NULL;
12223    char *pdata;
12224 
12225    /* compose bank list */
12226    bklist[0] = 0;
12227    nbk = 0;
12228    do {
12229       /* scan all banks for bank name only */
12230       if (bk_is32(event)) {
12231          size = bk_iterate32(event, &pmbk32, &pdata);
12232          if (pmbk32 == NULL)
12233             break;
12234       } else {
12235          size = bk_iterate(event, &pmbk, &pdata);
12236          if (pmbk == NULL)
12237             break;
12238       }
12239       nbk++;
12240 
12241       if (nbk > BANKLIST_MAX) {
12242          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
12243          return (nbk - 1);
12244       }
12245       if (bk_is32(event))
12246          strncat(bklist, (char *) pmbk32->name, 4);
12247       else
12248          strncat(bklist, (char *) pmbk->name, 4);
12249    }
12250    while (1);
12251    return (nbk);
12252 }
12253 
12254 /********************************************************************/
12255 /**
12256 Locates a MIDAS bank of given name inside an event.
12257 @param event pointer to current composed event
12258 @param name bank name to look for
12259 @param pdata pointer to data area of bank, NULL if bank not found
12260 @return number of values inside the bank
12261 */
12262 INT bk_locate(void *event, const char *name, void *pdata)
12263 {
12264    BANK *pbk;
12265    BANK32 *pbk32;
12266    DWORD dname;
12267 
12268    if (bk_is32(event)) {
12269       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12270       strncpy((char *) &dname, name, 4);
12271       while ((DWORD) ((char *) pbk32 - (char *) event) <
12272              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12273          if (*((DWORD *) pbk32->name) == dname) {
12274             *((void **) pdata) = pbk32 + 1;
12275             if (tid_size[pbk32->type & 0xFF] == 0)
12276                return pbk32->data_size;
12277             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
12278          }
12279          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12280       }
12281    } else {
12282       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12283       strncpy((char *) &dname, name, 4);
12284       while ((DWORD) ((char *) pbk - (char *) event) <
12285              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12286          if (*((DWORD *) pbk->name) == dname) {
12287             *((void **) pdata) = pbk + 1;
12288             if (tid_size[pbk->type & 0xFF] == 0)
12289                return pbk->data_size;
12290             return pbk->data_size / tid_size[pbk->type & 0xFF];
12291          }
12292          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12293       }
12294 
12295    }
12296 
12297    /* bank not found */
12298    *((void **) pdata) = NULL;
12299    return 0;
12300 }
12301 
12302 /********************************************************************/
12303 /**
12304 Finds a MIDAS bank of given name inside an event.
12305 @param pbkh pointer to current composed event
12306 @param name bank name to look for
12307 @param bklen number of elemtents in bank
12308 @param bktype bank type, one of TID_xxx
12309 @param pdata pointer to data area of bank, NULL if bank not found
12310 @return 1 if bank found, 0 otherwise
12311 */
12312 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype, void **pdata)
12313 {
12314    BANK *pbk;
12315    BANK32 *pbk32;
12316    DWORD dname;
12317 
12318    if (bk_is32(pbkh)) {
12319       pbk32 = (BANK32 *) (pbkh + 1);
12320       strncpy((char *) &dname, name, 4);
12321       do {
12322          if (*((DWORD *) pbk32->name) == dname) {
12323             *((void **) pdata) = pbk32 + 1;
12324             if (tid_size[pbk32->type & 0xFF] == 0)
12325                *bklen = pbk32->data_size;
12326             else
12327                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
12328 
12329             *bktype = pbk32->type;
12330             return 1;
12331          }
12332          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12333       } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
12334    } else {
12335       pbk = (BANK *) (pbkh + 1);
12336       strncpy((char *) &dname, name, 4);
12337       do {
12338          if (*((DWORD *) pbk->name) == dname) {
12339             *((void **) pdata) = pbk + 1;
12340             if (tid_size[pbk->type & 0xFF] == 0)
12341                *bklen = pbk->data_size;
12342             else
12343                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
12344 
12345             *bktype = pbk->type;
12346             return 1;
12347          }
12348          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12349       } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
12350    }
12351 
12352    /* bank not found */
12353    *((void **) pdata) = NULL;
12354    return 0;
12355 }
12356 
12357 /********************************************************************/
12358 /**
12359 Iterates through banks inside an event.
12360 The function can be used to enumerate all banks of an event.
12361 The returned pointer to the bank header has following structure:
12362 \code
12363 typedef struct {
12364 char   name[4];
12365 WORD   type;
12366 WORD   data_size;
12367 } BANK;
12368 \endcode
12369 where type is a TID_xxx value and data_size the size of the bank in bytes.
12370 \code
12371 BANK *pbk;
12372 INT  size;
12373 void *pdata;
12374 char name[5];
12375 pbk = NULL;
12376 do
12377 {
12378  size = bk_iterate(event, &pbk, &pdata);
12379  if (pbk == NULL)
12380   break;
12381  *((DWORD *)name) = *((DWORD *)(pbk)->name);
12382  name[4] = 0;
12383  printf("bank %s found\n", name);
12384 } while(TRUE);
12385 \endcode
12386 @param event Pointer to data area of event.
12387 @param pbk pointer to the bank header, must be NULL for the first call to
12388 this function.
12389 @param pdata Pointer to the bank header, must be NULL for the first
12390 call to this function
12391 @return Size of bank in bytes
12392 */
12393 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
12394 {
12395    if (*pbk == NULL)
12396       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12397    else
12398       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12399 
12400    *((void **) pdata) = (*pbk) + 1;
12401 
12402    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12403       *pbk = *((BANK **) pdata) = NULL;
12404       return 0;
12405    }
12406 
12407    return (*pbk)->data_size;
12408 }
12409 
12410 
12411 /**dox***************************************************************/
12412 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12413 
12414 /********************************************************************/
12415 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
12416 /********************************************************************\
12417 
12418   Routine: bk_iterate
12419 
12420   Purpose: Iterate through 32 bit MIDAS banks inside an event
12421 
12422   Input:
12423     void   *event           pointer to the event
12424     BANK   **pbk32          must be NULL for the first call to bk_iterate
12425 
12426   Output:
12427     BANK   **pbk            pointer to the bank header
12428     void   *pdata           pointer to data area of the bank
12429 
12430   Function value:
12431     INT    size of the bank in bytes
12432 
12433 \********************************************************************/
12434 {
12435    if (*pbk == NULL)
12436       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
12437    else
12438       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12439 
12440    *((void **) pdata) = (*pbk) + 1;
12441 
12442    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12443       *pbk = *((BANK32 **) pdata) = NULL;
12444       return 0;
12445    }
12446 
12447    return (*pbk)->data_size;
12448 }
12449 
12450 /**dox***************************************************************/
12451 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12452 
12453 /********************************************************************/
12454 /**
12455 Swaps bytes from little endian to big endian or vice versa for a whole event.
12456 
12457 An event contains a flag which is set by bk_init() to identify
12458 the endian format of an event. If force is FALSE, this flag is evaluated and
12459 the event is only swapped if it is in the "wrong" format for this system.
12460 An event can be swapped to the "wrong" format on purpose for example by a
12461 front-end which wants to produce events in a "right" format for a back-end
12462 analyzer which has different byte ordering.
12463 @param event pointer to data area of event
12464 @param force If TRUE, the event is always swapped, if FALSE, the event
12465 is only swapped if it is in the wrong format.
12466 @return 1==event has been swap, 0==event has not been swapped.
12467 */
12468 INT bk_swap(void *event, BOOL force)
12469 {
12470    BANK_HEADER *pbh;
12471    BANK *pbk;
12472    BANK32 *pbk32;
12473    void *pdata;
12474    WORD type;
12475    BOOL b32;
12476 
12477    pbh = (BANK_HEADER *) event;
12478 
12479    /* only swap if flags in high 16-bit */
12480    if (pbh->flags < 0x10000 && !force)
12481       return 0;
12482 
12483    /* swap bank header */
12484    DWORD_SWAP(&pbh->data_size);
12485    DWORD_SWAP(&pbh->flags);
12486 
12487    /* check for 32bit banks */
12488    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
12489 
12490    pbk = (BANK *) (pbh + 1);
12491    pbk32 = (BANK32 *) pbk;
12492 
12493    /* scan event */
12494    while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
12495       /* swap bank header */
12496       if (b32) {
12497          DWORD_SWAP(&pbk32->type);
12498          DWORD_SWAP(&pbk32->data_size);
12499          pdata = pbk32 + 1;
12500          type = (WORD) pbk32->type;
12501       } else {
12502          WORD_SWAP(&pbk->type);
12503          WORD_SWAP(&pbk->data_size);
12504          pdata = pbk + 1;
12505          type = pbk->type;
12506       }
12507 
12508       /* pbk points to next bank */
12509       if (b32) {
12510          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12511          pbk = (BANK *) pbk32;
12512       } else {
12513          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12514          pbk32 = (BANK32 *) pbk;
12515       }
12516 
12517       switch (type) {
12518       case TID_WORD:
12519       case TID_SHORT:
12520          while ((char *) pdata < (char *) pbk) {
12521             WORD_SWAP(pdata);
12522             pdata = (void *) (((WORD *) pdata) + 1);
12523          }
12524          break;
12525 
12526       case TID_DWORD:
12527       case TID_INT:
12528       case TID_BOOL:
12529       case TID_FLOAT:
12530          while ((char *) pdata < (char *) pbk) {
12531             DWORD_SWAP(pdata);
12532             pdata = (void *) (((DWORD *) pdata) + 1);
12533          }
12534          break;
12535 
12536       case TID_DOUBLE:
12537          while ((char *) pdata < (char *) pbk) {
12538             QWORD_SWAP(pdata);
12539             pdata = (void *) (((double *) pdata) + 1);
12540          }
12541          break;
12542       }
12543    }
12544 
12545    return CM_SUCCESS;
12546 }
12547 
12548 /**dox***************************************************************/
12549                                                                                                              /** @} *//* end of bkfunctionc */
12550 
12551 /**dox***************************************************************/
12552 /** @addtogroup hsfunctionc
12553  *
12554  *  @{  */
12555 
12556 #if !defined(OS_VXWORKS)
12557 /********************************************************************\
12558 *                                                                    *
12559 *                 History functions                                  *
12560 *                                                                    *
12561 \********************************************************************/
12562 
12563 /**dox***************************************************************/
12564 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12565 
12566 static HISTORY *_history;
12567 static INT _history_entries = 0;
12568 static char _hs_path_name[MAX_STRING_LENGTH];
12569 
12570 /**dox***************************************************************/
12571 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12572 
12573 /********************************************************************/
12574 /**
12575 Sets the path for future history file accesses. Should
12576 be called before any other history function is called.
12577 @param path             Directory where history files reside
12578 @return HS_SUCCESS
12579 */
12580 INT hs_set_path(char *path)
12581 {
12582    /* set path locally and remotely */
12583    if (rpc_is_remote())
12584       rpc_call(RPC_HS_SET_PATH, path);
12585 
12586    strcpy(_hs_path_name, path);
12587 
12588    /* check for trailing directory seperator */
12589    if (strlen(_hs_path_name) > 0 && _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
12590       strcat(_hs_path_name, DIR_SEPARATOR_STR);
12591 
12592    return HS_SUCCESS;
12593 }
12594 
12595 
12596 /********************************************************************/
12597 /**
12598 Open history file belonging to certain date. Internal use
12599            only.
12600 @param ltime          Date for which a history file should be opened.
12601 @param suffix         File name suffix like "hst", "idx", "idf"
12602 @param mode           R/W access mode
12603 @param fh             File handle
12604 @return HS_SUCCESS
12605 */
12606 INT hs_open_file(time_t ltime, char *suffix, INT mode, int *fh)
12607 {
12608    struct tm *tms;
12609    char file_name[256];
12610    time_t ttime;
12611 
12612    /* generate new file name YYMMDD.xxx */
12613 #if !defined(OS_VXWORKS)
12614 #if !defined(OS_VMS)
12615    tzset();
12616 #endif
12617 #endif
12618    ttime = (time_t) ltime;
12619    tms = localtime(&ttime);
12620 
12621    sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name,
12622            tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
12623 
12624    /* open file, add O_BINARY flag for Windows NT */
12625    *fh = open(file_name, mode | O_BINARY, 0644);
12626 
12627    return HS_SUCCESS;
12628 }
12629 
12630 /**dox***************************************************************/
12631 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12632 
12633 /********************************************************************/
12634 INT hs_gen_index(DWORD ltime)
12635 /********************************************************************\
12636 
12637   Routine: hs_gen_index
12638 
12639   Purpose: Regenerate index files ("idx" and "idf" files) for a given
12640            history file ("hst"). Interal use only.
12641 
12642   Input:
12643     time_t ltime            Date for which a history file should
12644                             be analyzed.
12645 
12646   Output:
12647     none
12648 
12649   Function value:
12650     HS_SUCCESS              Successful completion
12651     HS_FILE_ERROR           Index files cannot be created
12652 
12653 \********************************************************************/
12654 {
12655    char event_name[NAME_LENGTH];
12656    int fh, fhd, fhi;
12657    INT n;
12658    HIST_RECORD rec;
12659    INDEX_RECORD irec;
12660    DEF_RECORD def_rec;
12661 
12662    printf("Recovering index files...\n");
12663 
12664    if (ltime == 0)
12665       ltime = time(NULL);
12666 
12667    /* open new index file */
12668    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
12669    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
12670 
12671    if (fhd < 0 || fhi < 0) {
12672       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
12673       return HS_FILE_ERROR;
12674    }
12675 
12676    /* open history file */
12677    hs_open_file(ltime, "hst", O_RDONLY, &fh);
12678    if (fh < 0)
12679       return HS_FILE_ERROR;
12680    lseek(fh, 0, SEEK_SET);
12681 
12682    /* loop over file records in .hst file */
12683    do {
12684       n = read(fh, (char *) &rec, sizeof(rec));
12685       if (n < sizeof(rec))
12686          break;
12687 
12688       /* check if record type is definition */
12689       if (rec.record_type == RT_DEF) {
12690          /* read name */
12691          read(fh, event_name, sizeof(event_name));
12692 
12693          printf("Event definition %s, ID %d\n", event_name, rec.event_id);
12694 
12695          /* write definition index record */
12696          def_rec.event_id = rec.event_id;
12697          memcpy(def_rec.event_name, event_name, sizeof(event_name));
12698          def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
12699          write(fhd, (char *) &def_rec, sizeof(def_rec));
12700 
12701          /* skip tags */
12702          lseek(fh, rec.data_size, SEEK_CUR);
12703       } else {
12704          /* write index record */
12705          irec.event_id = rec.event_id;
12706          irec.time = rec.time;
12707          irec.offset = TELL(fh) - sizeof(rec);
12708          write(fhi, (char *) &irec, sizeof(irec));
12709 
12710          /* skip data */
12711          lseek(fh, rec.data_size, SEEK_CUR);
12712       }
12713 
12714    } while (TRUE);
12715 
12716    close(fh);
12717    close(fhi);
12718    close(fhd);
12719 
12720    printf("...done.\n");
12721 
12722    return HS_SUCCESS;
12723 }
12724 
12725 
12726 /********************************************************************/
12727 INT hs_search_file(DWORD * ltime, INT direction)
12728 /********************************************************************\
12729 
12730   Routine: hs_search_file
12731 
12732   Purpose: Search an history file for a given date. If not found,
12733            look for files after date (direction==1) or before date
12734            (direction==-1) up to one year.
12735 
12736   Input:
12737     DWORD  *ltime           Date of history file
12738     INT    direction        Search direction
12739 
12740   Output:
12741     DWORD  *ltime           Date of history file found
12742 
12743   Function value:
12744     HS_SUCCESS              Successful completion
12745     HS_FILE_ERROR           No file found
12746 
12747 \********************************************************************/
12748 {
12749    time_t lt;
12750    int fh, fhd, fhi;
12751    struct tm *tms;
12752 
12753    if (*ltime == 0)
12754       *ltime = ss_time();
12755 
12756    lt = (time_t) * ltime;
12757    do {
12758       /* try to open history file for date "lt" */
12759       hs_open_file(lt, "hst", O_RDONLY, &fh);
12760 
12761       /* if not found, look for next day */
12762       if (fh < 0)
12763          lt += direction * 3600 * 24;
12764 
12765       /* stop if more than a year before starting point or in the future */
12766    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 && lt <= (time_t) ss_time());
12767 
12768    if (fh < 0)
12769       return HS_FILE_ERROR;
12770 
12771    if (lt != *ltime) {
12772       /* if switched to new day, set start_time to 0:00 */
12773       tms = localtime(&lt);
12774       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
12775       *ltime = mktime(tms);
12776    }
12777 
12778    /* check if index files are there */
12779    hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
12780    hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
12781 
12782    close(fh);
12783    close(fhd);
12784    close(fhi);
12785 
12786    /* generate them if not */
12787    if (fhd < 0 || fhi < 0)
12788       hs_gen_index(*ltime);
12789 
12790    return HS_SUCCESS;
12791 }
12792 
12793 
12794 /********************************************************************/
12795 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
12796 /********************************************************************\
12797 
12798   Routine: hs_define_evnet
12799 
12800   Purpose: Define a new event for which a history should be recorded.
12801            This routine must be called before any call to
12802            hs_write_event. It also should be called if the definition
12803            of the event has changed.
12804 
12805            The event definition is written directly to the history
12806            file. If the definition is identical to a previous
12807            definition, it is not written to the file.
12808 
12809 
12810   Input:
12811     DWORD  event_id         ID for this event. Must be unique.
12812     char   name             Name of this event
12813     TAG    tag              Tag list containing names and types of
12814                             variables in this event.
12815     DWORD  size             Size of tag array
12816 
12817   Output:
12818     <none>
12819 
12820   Function value:
12821     HS_SUCCESS              Successful completion
12822     HS_NO_MEMEORY           Out of memory
12823     HS_FILE_ERROR           Cannot open history file
12824 
12825 \********************************************************************/
12826 {
12827 /* History events are only written locally (?)
12828 
12829   if (rpc_is_remote())
12830     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
12831 */
12832    {
12833       HIST_RECORD rec, prev_rec;
12834       DEF_RECORD def_rec;
12835       time_t ltime;
12836       char str[256], event_name[NAME_LENGTH], *buffer;
12837       int fh, fhi, fhd;
12838       INT i, n, len, index;
12839       struct tm *tmb;
12840 
12841       /* allocate new space for the new history descriptor */
12842       if (_history_entries == 0) {
12843          _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
12844          memset(_history, 0, sizeof(HISTORY));
12845          if (_history == NULL)
12846             return HS_NO_MEMORY;
12847 
12848          _history_entries = 1;
12849          index = 0;
12850       } else {
12851          /* check if history already open */
12852          for (i = 0; i < _history_entries; i++)
12853             if (_history[i].event_id == event_id)
12854                break;
12855 
12856          /* if not found, create new one */
12857          if (i == _history_entries) {
12858             _history = (HISTORY *) realloc(_history, sizeof(HISTORY) * (_history_entries + 1));
12859             memset(&_history[_history_entries], 0, sizeof(HISTORY));
12860 
12861             _history_entries++;
12862             if (_history == NULL) {
12863                _history_entries--;
12864                return HS_NO_MEMORY;
12865             }
12866          }
12867          index = i;
12868       }
12869 
12870       /* assemble definition record header */
12871       rec.record_type = RT_DEF;
12872       rec.event_id = event_id;
12873       rec.time = time(NULL);
12874       rec.data_size = size;
12875       strncpy(event_name, name, NAME_LENGTH);
12876 
12877       /* pad tag names with zeos */
12878       for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
12879          len = strlen(tag[i].name);
12880          memset(tag[i].name + len, 0, NAME_LENGTH - len);
12881       }
12882 
12883       /* if history structure not set up, do so now */
12884       if (!_history[index].hist_fh) {
12885          /* open history file */
12886          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
12887          if (fh < 0)
12888             return HS_FILE_ERROR;
12889 
12890          /* open index files */
12891          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
12892          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
12893          lseek(fh, 0, SEEK_END);
12894          lseek(fhi, 0, SEEK_END);
12895          lseek(fhd, 0, SEEK_END);
12896 
12897          /* regenerate index if missing */
12898          if (TELL(fh) > 0 && TELL(fhd) == 0) {
12899             close(fh);
12900             close(fhi);
12901             close(fhd);
12902             hs_gen_index(rec.time);
12903             hs_open_file(rec.time, "hst", O_RDWR, &fh);
12904             hs_open_file(rec.time, "idx", O_RDWR, &fhi);
12905             hs_open_file(rec.time, "idf", O_RDWR, &fhd);
12906             lseek(fh, 0, SEEK_END);
12907             lseek(fhi, 0, SEEK_END);
12908             lseek(fhd, 0, SEEK_END);
12909          }
12910 
12911          ltime = (time_t) rec.time;
12912          tmb = localtime(&ltime);
12913          tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
12914 
12915          /* setup history structure */
12916          _history[index].hist_fh = fh;
12917          _history[index].index_fh = fhi;
12918          _history[index].def_fh = fhd;
12919          _history[index].def_offset = TELL(fh);
12920          _history[index].event_id = event_id;
12921          strcpy(_history[index].event_name, event_name);
12922          _history[index].base_time = mktime(tmb);
12923          _history[index].n_tag = size / sizeof(TAG);
12924          _history[index].tag = (TAG *) M_MALLOC(size);
12925          memcpy(_history[index].tag, tag, size);
12926 
12927          /* search previous definition */
12928          n = TELL(fhd) / sizeof(def_rec);
12929          def_rec.event_id = 0;
12930          for (i = n - 1; i >= 0; i--) {
12931             lseek(fhd, i * sizeof(def_rec), SEEK_SET);
12932             read(fhd, (char *) &def_rec, sizeof(def_rec));
12933             if (def_rec.event_id == event_id)
12934                break;
12935          }
12936          lseek(fhd, 0, SEEK_END);
12937 
12938          /* if definition found, compare it with new one */
12939          if (def_rec.event_id == event_id) {
12940             buffer = (char *) M_MALLOC(size);
12941             memset(buffer, 0, size);
12942 
12943             lseek(fh, def_rec.def_offset, SEEK_SET);
12944             read(fh, (char *) &prev_rec, sizeof(prev_rec));
12945             read(fh, str, NAME_LENGTH);
12946             read(fh, buffer, size);
12947             lseek(fh, 0, SEEK_END);
12948 
12949             if (prev_rec.data_size != size || strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
12950                /* write definition to history file */
12951                write(fh, (char *) &rec, sizeof(rec));
12952                write(fh, event_name, NAME_LENGTH);
12953                write(fh, (char *) tag, size);
12954 
12955                /* write index record */
12956                def_rec.event_id = event_id;
12957                memcpy(def_rec.event_name, event_name, sizeof(event_name));
12958                def_rec.def_offset = _history[index].def_offset;
12959                write(fhd, (char *) &def_rec, sizeof(def_rec));
12960             } else
12961                /* definition identical, just remember old offset */
12962                _history[index].def_offset = def_rec.def_offset;
12963 
12964             M_FREE(buffer);
12965          } else {
12966             /* write definition to history file */
12967             write(fh, (char *) &rec, sizeof(rec));
12968             write(fh, event_name, NAME_LENGTH);
12969             write(fh, (char *) tag, size);
12970 
12971             /* write definition index record */
12972             def_rec.event_id = event_id;
12973             memcpy(def_rec.event_name, event_name, sizeof(event_name));
12974             def_rec.def_offset = _history[index].def_offset;
12975             write(fhd, (char *) &def_rec, sizeof(def_rec));
12976          }
12977       } else {
12978          fh = _history[index].hist_fh;
12979          fhd = _history[index].def_fh;
12980 
12981          /* compare definition with previous definition */
12982          buffer = (char *) M_MALLOC(size);
12983          memset(buffer, 0, size);
12984 
12985          lseek(fh, _history[index].def_offset, SEEK_SET);
12986          read(fh, (char *) &prev_rec, sizeof(prev_rec));
12987          read(fh, str, NAME_LENGTH);
12988          read(fh, buffer, size);
12989 
12990          lseek(fh, 0, SEEK_END);
12991          lseek(fhd, 0, SEEK_END);
12992 
12993          if (prev_rec.data_size != size || strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
12994             /* save new definition offset */
12995             _history[index].def_offset = TELL(fh);
12996 
12997             /* write definition to history file */
12998             write(fh, (char *) &rec, sizeof(rec));
12999             write(fh, event_name, NAME_LENGTH);
13000             write(fh, (char *) tag, size);
13001 
13002             /* write index record */
13003             def_rec.event_id = event_id;
13004             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13005             def_rec.def_offset = _history[index].def_offset;
13006             write(fhd, (char *) &def_rec, sizeof(def_rec));
13007          }
13008 
13009          M_FREE(buffer);
13010       }
13011 
13012    }
13013 
13014    return HS_SUCCESS;
13015 }
13016 
13017 
13018 /********************************************************************/
13019 INT hs_write_event(DWORD event_id, void *data, DWORD size)
13020 /********************************************************************\
13021 
13022   Routine: hs_write_event
13023 
13024   Purpose: Write an event to a history file.
13025 
13026   Input:
13027     DWORD  event_id         Event ID
13028     void   *data            Data buffer containing event
13029     DWORD  size             Data buffer size in bytes
13030 
13031   Output:
13032     none
13033                             future hs_write_event
13034 
13035   Function value:
13036     HS_SUCCESS              Successful completion
13037     HS_NO_MEMEORY           Out of memory
13038     HS_FILE_ERROR           Cannot write to history file
13039     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
13040 
13041 \********************************************************************/
13042 {
13043 /* history events are only written locally (?)
13044 
13045   if (rpc_is_remote())
13046     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
13047 */
13048    HIST_RECORD rec, drec;
13049    DEF_RECORD def_rec;
13050    INDEX_RECORD irec;
13051    int fh, fhi, fhd;
13052    INT index;
13053    struct tm tmb, tmr;
13054    time_t ltime;
13055 
13056    /* find index to history structure */
13057    for (index = 0; index < _history_entries; index++)
13058       if (_history[index].event_id == event_id)
13059          break;
13060    if (index == _history_entries)
13061       return HS_UNDEFINED_EVENT;
13062 
13063    /* assemble record header */
13064    rec.record_type = RT_DATA;
13065    rec.event_id = _history[index].event_id;
13066    rec.time = time(NULL);
13067    rec.def_offset = _history[index].def_offset;
13068    rec.data_size = size;
13069 
13070    irec.event_id = _history[index].event_id;
13071    irec.time = rec.time;
13072 
13073    /* check if new day */
13074    ltime = (time_t) rec.time;
13075    memcpy(&tmr, localtime(&ltime), sizeof(tmr));
13076    ltime = (time_t) _history[index].base_time;
13077    memcpy(&tmb, localtime(&ltime), sizeof(tmb));
13078 
13079    if (tmr.tm_yday != tmb.tm_yday) {
13080       /* close current history file */
13081       close(_history[index].hist_fh);
13082       close(_history[index].def_fh);
13083       close(_history[index].index_fh);
13084 
13085       /* open new history file */
13086       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13087       if (fh < 0)
13088          return HS_FILE_ERROR;
13089 
13090       /* open new index file */
13091       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13092       if (fhi < 0)
13093          return HS_FILE_ERROR;
13094 
13095       /* open new definition index file */
13096       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13097       if (fhd < 0)
13098          return HS_FILE_ERROR;
13099 
13100       lseek(fh, 0, SEEK_END);
13101       lseek(fhi, 0, SEEK_END);
13102       lseek(fhd, 0, SEEK_END);
13103 
13104       /* remember new file handles */
13105       _history[index].hist_fh = fh;
13106       _history[index].index_fh = fhi;
13107       _history[index].def_fh = fhd;
13108 
13109       _history[index].def_offset = TELL(fh);
13110       rec.def_offset = _history[index].def_offset;
13111 
13112       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
13113       _history[index].base_time = mktime(&tmr);
13114 
13115       /* write definition from _history structure */
13116       drec.record_type = RT_DEF;
13117       drec.event_id = _history[index].event_id;
13118       drec.time = rec.time;
13119       drec.data_size = _history[index].n_tag * sizeof(TAG);
13120 
13121       write(fh, (char *) &drec, sizeof(drec));
13122       write(fh, _history[index].event_name, NAME_LENGTH);
13123       write(fh, (char *) _history[index].tag, drec.data_size);
13124 
13125       /* write definition index record */
13126       def_rec.event_id = _history[index].event_id;
13127       memcpy(def_rec.event_name, _history[index].event_name, sizeof(def_rec.event_name));
13128       def_rec.def_offset = _history[index].def_offset;
13129       write(fhd, (char *) &def_rec, sizeof(def_rec));
13130    }
13131 
13132    /* got to end of file */
13133    lseek(_history[index].hist_fh, 0, SEEK_END);
13134    irec.offset = TELL(_history[index].hist_fh);
13135 
13136    /* write record header */
13137    write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
13138 
13139    /* write data */
13140    write(_history[index].hist_fh, (char *) data, size);
13141 
13142    /* write index record */
13143    lseek(_history[index].index_fh, 0, SEEK_END);
13144    if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) < sizeof(irec))
13145       return HS_FILE_ERROR;
13146 
13147    return HS_SUCCESS;
13148 }
13149 
13150 
13151 /********************************************************************/
13152 INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size, INT event_id[], DWORD * id_size)
13153 /********************************************************************\
13154 
13155   Routine: hs_enum_events
13156 
13157   Purpose: Enumerate events for a given date
13158 
13159   Input:
13160     DWORD  ltime            Date at which events should be enumerated
13161 
13162   Output:
13163     char   *event_name      Array containing event names
13164     DWORD  *name_size       Size of name array
13165     char   *event_id        Array containing event IDs
13166     DWORD  *id_size         Size of ID array
13167 
13168   Function value:
13169     HS_SUCCESS              Successful completion
13170     HS_NO_MEMEORY           Out of memory
13171     HS_FILE_ERROR           Cannot open history file
13172 
13173 \********************************************************************/
13174 {
13175    int fh, fhd;
13176    INT status, i, j, n;
13177    DEF_RECORD def_rec;
13178 
13179    if (rpc_is_remote())
13180       return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name, name_size, event_id, id_size);
13181 
13182    /* search latest history file */
13183    status = hs_search_file(&ltime, -1);
13184    if (status != HS_SUCCESS) {
13185       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
13186       return HS_FILE_ERROR;
13187    }
13188 
13189    /* open history and definition files */
13190    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13191    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13192    if (fh < 0 || fhd < 0) {
13193       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
13194       return HS_FILE_ERROR;
13195    }
13196    lseek(fhd, 0, SEEK_SET);
13197 
13198    /* loop over definition index file */
13199    n = 0;
13200    do {
13201       /* read event definition */
13202       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13203       if (j < (int) sizeof(def_rec))
13204          break;
13205 
13206       /* look for existing entry for this event id */
13207       for (i = 0; i < n; i++)
13208          if (event_id[i] == (INT) def_rec.event_id) {
13209             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13210             break;
13211          }
13212 
13213       /* new entry found */
13214       if (i == n) {
13215          if (i * NAME_LENGTH > (INT) * name_size || i * sizeof(INT) > (INT) * id_size) {
13216             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
13217             close(fh);
13218             close(fhd);
13219             return HS_NO_MEMORY;
13220          }
13221 
13222          /* copy definition record */
13223          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13224          event_id[i] = def_rec.event_id;
13225          n++;
13226       }
13227    } while (TRUE);
13228 
13229    close(fh);
13230    close(fhd);
13231    *name_size = n * NAME_LENGTH;
13232    *id_size = n * sizeof(INT);
13233 
13234    return HS_SUCCESS;
13235 }
13236 
13237 
13238 /********************************************************************/
13239 INT hs_count_events(DWORD ltime, DWORD * count)
13240 /********************************************************************\
13241 
13242   Routine: hs_count_events
13243 
13244   Purpose: Count number of different events for a given date
13245 
13246   Input:
13247     DWORD  ltime            Date at which events should be counted
13248 
13249   Output:
13250     DWORD  *count           Number of different events found
13251 
13252   Function value:
13253     HS_SUCCESS              Successful completion
13254     HS_FILE_ERROR           Cannot open history file
13255 
13256 \********************************************************************/
13257 {
13258    int fh, fhd;
13259    INT status, i, j, n;
13260    DWORD *id;
13261    DEF_RECORD def_rec;
13262 
13263    if (rpc_is_remote())
13264       return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
13265 
13266    /* search latest history file */
13267    status = hs_search_file(&ltime, -1);
13268    if (status != HS_SUCCESS) {
13269       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13270       return HS_FILE_ERROR;
13271    }
13272 
13273    /* open history and definition files */
13274    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13275    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13276    if (fh < 0 || fhd < 0) {
13277       cm_msg(MERROR, "hs_count_events", "cannot open index files");
13278       return HS_FILE_ERROR;
13279    }
13280 
13281    /* allocate event id array */
13282    lseek(fhd, 0, SEEK_END);
13283    id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
13284    lseek(fhd, 0, SEEK_SET);
13285 
13286    /* loop over index file */
13287    n = 0;
13288    do {
13289       /* read definition index record */
13290       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13291       if (j < (int) sizeof(def_rec))
13292          break;
13293 
13294       /* look for existing entries */
13295       for (i = 0; i < n; i++)
13296          if (id[i] == def_rec.event_id)
13297             break;
13298 
13299       /* new entry found */
13300       if (i == n) {
13301          id[i] = def_rec.event_id;
13302          n++;
13303       }
13304    } while (TRUE);
13305 
13306 
13307    M_FREE(id);
13308    close(fh);
13309    close(fhd);
13310    *count = n;
13311 
13312    return HS_SUCCESS;
13313 }
13314 
13315 
13316 /********************************************************************/
13317 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
13318 /********************************************************************\
13319 
13320   Routine: hs_get_event_id
13321 
13322   Purpose: Return event ID for a given name. If event cannot be found
13323            in current definition file, go back in time until found
13324 
13325   Input:
13326     DWORD  ltime            Date at which event ID should be looked for
13327 
13328   Output:
13329     DWORD  *id              Event ID
13330 
13331   Function value:
13332     HS_SUCCESS              Successful completion
13333     HS_FILE_ERROR           Cannot open history file
13334     HS_UNDEFINED_EVENT      Event "name" not found
13335 
13336 \********************************************************************/
13337 {
13338    int fh, fhd;
13339    INT status, i;
13340    DWORD lt;
13341    DEF_RECORD def_rec;
13342 
13343    if (rpc_is_remote())
13344       return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
13345 
13346    /* search latest history file */
13347    if (ltime == 0)
13348       ltime = time(NULL);
13349 
13350    lt = ltime;
13351 
13352    do {
13353       status = hs_search_file(&lt, -1);
13354       if (status != HS_SUCCESS) {
13355          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13356          return HS_FILE_ERROR;
13357       }
13358 
13359       /* open history and definition files */
13360       hs_open_file(lt, "hst", O_RDONLY, &fh);
13361       hs_open_file(lt, "idf", O_RDONLY, &fhd);
13362       if (fh < 0 || fhd < 0) {
13363          cm_msg(MERROR, "hs_count_events", "cannot open index files");
13364          return HS_FILE_ERROR;
13365       }
13366 
13367       /* loop over index file */
13368       *id = 0;
13369       do {
13370          /* read definition index record */
13371          i = read(fhd, (char *) &def_rec, sizeof(def_rec));
13372          if (i < (int) sizeof(def_rec))
13373             break;
13374 
13375          if (strcmp(name, def_rec.event_name) == 0) {
13376             *id = def_rec.event_id;
13377             close(fh);
13378             close(fhd);
13379             return HS_SUCCESS;
13380          }
13381       } while (TRUE);
13382 
13383       close(fh);
13384       close(fhd);
13385 
13386       /* not found -> go back one day */
13387       lt -= 3600 * 24;
13388 
13389    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
13390 
13391    return HS_UNDEFINED_EVENT;
13392 }
13393 
13394 
13395 /********************************************************************/
13396 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
13397 /********************************************************************\
13398 
13399   Routine: hs_count_vars
13400 
13401   Purpose: Count number of variables for a given date and event id
13402 
13403   Input:
13404     DWORD  ltime            Date at which tags should be counted
13405 
13406   Output:
13407     DWORD  *count           Number of tags
13408 
13409   Function value:
13410     HS_SUCCESS              Successful completion
13411     HS_FILE_ERROR           Cannot open history file
13412 
13413 \********************************************************************/
13414 {
13415    int fh, fhd;
13416    INT i, n, status;
13417    DEF_RECORD def_rec;
13418    HIST_RECORD rec;
13419 
13420    if (rpc_is_remote())
13421       return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
13422 
13423    /* search latest history file */
13424    status = hs_search_file(&ltime, -1);
13425    if (status != HS_SUCCESS) {
13426       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
13427       return HS_FILE_ERROR;
13428    }
13429 
13430    /* open history and definition files */
13431    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13432    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13433    if (fh < 0 || fhd < 0) {
13434       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
13435       return HS_FILE_ERROR;
13436    }
13437 
13438    /* search last definition */
13439    lseek(fhd, 0, SEEK_END);
13440    n = TELL(fhd) / sizeof(def_rec);
13441    def_rec.event_id = 0;
13442    for (i = n - 1; i >= 0; i--) {
13443       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13444       read(fhd, (char *) &def_rec, sizeof(def_rec));
13445       if (def_rec.event_id == event_id)
13446          break;
13447    }
13448    if (def_rec.event_id != event_id) {
13449       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
13450       return HS_FILE_ERROR;
13451    }
13452 
13453    /* read definition */
13454    lseek(fh, def_rec.def_offset, SEEK_SET);
13455    read(fh, (char *) &rec, sizeof(rec));
13456    *count = rec.data_size / sizeof(TAG);
13457 
13458    close(fh);
13459    close(fhd);
13460 
13461    return HS_SUCCESS;
13462 }
13463 
13464 
13465 /********************************************************************/
13466 INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD * size, DWORD * var_n, DWORD * n_size)
13467 /********************************************************************\
13468 
13469   Routine: hs_enum_vars
13470 
13471   Purpose: Enumerate variable tags for a given date and event id
13472 
13473   Input:
13474     DWORD  ltime            Date at which tags should be enumerated
13475     DWORD  event_id         Event ID
13476 
13477   Output:
13478     char   *var_name        Array containing variable names
13479     DWORD  *size            Size of name array
13480     DWORD  *var_n           Array size of variable
13481     DWORD  *n_size          Size of n array
13482 
13483   Function value:
13484     HS_SUCCESS              Successful completion
13485     HS_NO_MEMEORY           Out of memory
13486     HS_FILE_ERROR           Cannot open history file
13487 
13488 \********************************************************************/
13489 {
13490    char str[256];
13491    int fh, fhd;
13492    INT i, n, status;
13493    DEF_RECORD def_rec;
13494    HIST_RECORD rec;
13495    TAG *tag;
13496 
13497    if (rpc_is_remote())
13498       return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
13499 
13500    /* search latest history file */
13501    status = hs_search_file(&ltime, -1);
13502    if (status != HS_SUCCESS) {
13503       cm_msg(MERROR, "hs_enum_vars", "cannot find recent history file");
13504       return HS_FILE_ERROR;
13505    }
13506 
13507    /* open history and definition files */
13508    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13509    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13510    if (fh < 0 || fhd < 0) {
13511       cm_msg(MERROR, "hs_enum_vars", "cannot open index files");
13512       return HS_FILE_ERROR;
13513    }
13514 
13515    /* search last definition */
13516    lseek(fhd, 0, SEEK_END);
13517    n = TELL(fhd) / sizeof(def_rec);
13518    def_rec.event_id = 0;
13519    for (i = n - 1; i >= 0; i--) {
13520       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13521       read(fhd, (char *) &def_rec, sizeof(def_rec));
13522       if (def_rec.event_id == event_id)
13523          break;
13524    }
13525    if (def_rec.event_id != event_id) {
13526       cm_msg(MERROR, "hs_enum_vars", "event %d not found in index file", event_id);
13527       return HS_FILE_ERROR;
13528    }
13529 
13530    /* read definition header */
13531    lseek(fh, def_rec.def_offset, SEEK_SET);
13532    read(fh, (char *) &rec, sizeof(rec));
13533    read(fh, str, NAME_LENGTH);
13534 
13535    /* read event definition */
13536    n = rec.data_size / sizeof(TAG);
13537    tag = (TAG *) M_MALLOC(rec.data_size);
13538    read(fh, (char *) tag, rec.data_size);
13539 
13540    if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
13541 
13542       /* store partial definition */
13543       for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
13544          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13545          var_n[i] = tag[i].n_data;
13546       }
13547 
13548       cm_msg(MERROR, "hs_enum_vars", "tag buffer too small");
13549       M_FREE(tag);
13550       close(fh);
13551       close(fhd);
13552       return HS_NO_MEMORY;
13553    }
13554 
13555    /* store full definition */
13556    for (i = 0; i < n; i++) {
13557       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13558       var_n[i] = tag[i].n_data;
13559    }
13560    *size = n * NAME_LENGTH;
13561    *n_size = n * sizeof(DWORD);
13562 
13563    M_FREE(tag);
13564    close(fh);
13565    close(fhd);
13566 
13567    return HS_SUCCESS;
13568 }
13569 
13570 
13571 /********************************************************************/
13572 INT hs_get_var(DWORD ltime, DWORD event_id, char *var_name, DWORD * type, INT * n_data)
13573 /********************************************************************\
13574 
13575   Routine: hs_get_var
13576 
13577   Purpose: Get definition for certain variable
13578 
13579   Input:
13580     DWORD  ltime            Date at which variable definition should
13581                             be returned
13582     DWORD  event_id         Event ID
13583     char   *var_name        Name of variable
13584 
13585   Output:
13586     INT    *type            Type of variable
13587     INT    *n_data          Number of items in variable
13588 
13589   Function value:
13590     HS_SUCCESS              Successful completion
13591     HS_NO_MEMEORY           Out of memory
13592     HS_FILE_ERROR           Cannot open history file
13593 
13594 \********************************************************************/
13595 {
13596    char str[256];
13597    int fh, fhd;
13598    INT i, n, status;
13599    DEF_RECORD def_rec;
13600    HIST_RECORD rec;
13601    TAG *tag;
13602 
13603    if (rpc_is_remote())
13604       return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name, type, n_data);
13605 
13606    /* search latest history file */
13607    status = hs_search_file(&ltime, -1);
13608    if (status != HS_SUCCESS) {
13609       cm_msg(MERROR, "hs_get_var", "cannot find recent history file");
13610       return HS_FILE_ERROR;
13611    }
13612 
13613    /* open history and definition files */
13614    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13615    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13616    if (fh < 0 || fhd < 0) {
13617       cm_msg(MERROR, "hs_get_var", "cannot open index files");
13618       return HS_FILE_ERROR;
13619    }
13620 
13621    /* search last definition */
13622    lseek(fhd, 0, SEEK_END);
13623    n = TELL(fhd) / sizeof(def_rec);
13624    def_rec.event_id = 0;
13625    for (i = n - 1; i >= 0; i--) {
13626       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13627       read(fhd, (char *) &def_rec, sizeof(def_rec));
13628       if (def_rec.event_id == event_id)
13629          break;
13630    }
13631    if (def_rec.event_id != event_id) {
13632       cm_msg(MERROR, "hs_get_var", "event %d not found in index file", event_id);
13633       return HS_FILE_ERROR;
13634    }
13635 
13636    /* read definition header */
13637    lseek(fh, def_rec.def_offset, SEEK_SET);
13638    read(fh, (char *) &rec, sizeof(rec));
13639    read(fh, str, NAME_LENGTH);
13640 
13641    /* read event definition */
13642    n = rec.data_size / sizeof(TAG);
13643    tag = (TAG *) M_MALLOC(rec.data_size);
13644    read(fh, (char *) tag, rec.data_size);
13645 
13646    /* search variable */
13647    for (i = 0; i < n; i++)
13648       if (strcmp(tag[i].name, var_name) == 0)
13649          break;
13650 
13651    close(fh);
13652    close(fhd);
13653 
13654    if (i < n) {
13655       *type = tag[i].type;
13656       *n_data = tag[i].n_data;
13657    } else {
13658       *type = *n_data = 0;
13659       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
13660       M_FREE(tag);
13661       return HS_UNDEFINED_VAR;
13662    }
13663 
13664    M_FREE(tag);
13665    return HS_SUCCESS;
13666 }
13667 
13668 
13669 /********************************************************************/
13670 INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time,
13671             DWORD interval, char *tag_name, DWORD var_index,
13672             DWORD * time_buffer, DWORD * tbsize, void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
13673 /********************************************************************\
13674 
13675   Routine: hs_read
13676 
13677   Purpose: Read history for a variable at a certain time interval
13678 
13679   Input:
13680     DWORD  event_id         Event ID
13681     DWORD  start_time       Starting Date/Time
13682     DWORD  end_time         End Date/Time
13683     DWORD  interval         Minimum time in seconds between reported
13684                             events. Can be used to skip events
13685     char   *tag_name        Variable name inside event
13686     DWORD  var_index        Index if variable is array
13687 
13688   Output:
13689     DWORD  *time_buffer     Buffer containing times for each value
13690     DWORD  *tbsize          Size of time buffer
13691     void   *data_buffer     Buffer containing variable values
13692     DWORD  *dbsize          Data buffer size
13693     DWORD  *type            Type of variable (one of TID_xxx)
13694     DWORD  *n               Number of time/value pairs found
13695                             in specified interval and placed into
13696                             time_buffer and data_buffer
13697 
13698 
13699   Function value:
13700     HS_SUCCESS              Successful completion
13701     HS_NO_MEMEORY           Out of memory
13702     HS_FILE_ERROR           Cannot open history file
13703     HS_WRONG_INDEX          var_index exceeds array size of variable
13704     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
13705     HS_TRUNCATED            Buffer too small, data has been truncated
13706 
13707 \********************************************************************/
13708 {
13709    DWORD prev_time, last_irec_time;
13710    int fh, fhd, fhi, cp = 0;
13711    INT i, delta, index = 0, status, cache_size;
13712    INDEX_RECORD irec, *pirec;
13713    HIST_RECORD rec, drec;
13714    INT old_def_offset, var_size = 0, var_offset = 0;
13715    TAG *tag;
13716    char str[NAME_LENGTH];
13717    struct tm *tms;
13718    char *cache;
13719    time_t ltime;
13720 
13721    if (rpc_is_remote())
13722       return rpc_call(RPC_HS_READ, event_id, start_time, end_time, interval,
13723                       tag_name, var_index, time_buffer, tbsize, data_buffer, dbsize, type, n);
13724 
13725    /* if not time given, use present to one hour in past */
13726    if (start_time == 0)
13727       start_time = time(NULL) - 3600;
13728    if (end_time == 0)
13729       end_time = time(NULL);
13730 
13731    /* search history file for start_time */
13732    status = hs_search_file(&start_time, 1);
13733    if (status != HS_SUCCESS) {
13734       cm_msg(MERROR, "hs_read", "cannot find recent history file");
13735       *tbsize = *dbsize = *n = 0;
13736       return HS_FILE_ERROR;
13737    }
13738 
13739    /* open history and definition files */
13740    hs_open_file(start_time, "hst", O_RDONLY, &fh);
13741    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
13742    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
13743    if (fh < 0 || fhd < 0 || fhi < 0) {
13744       cm_msg(MERROR, "hs_read", "cannot open index files");
13745       *tbsize = *dbsize = *n = 0;
13746       return HS_FILE_ERROR;
13747    }
13748 
13749    /* try to read index file into cache */
13750    lseek(fhi, 0, SEEK_END);
13751    cache_size = TELL(fhi);
13752 
13753    if (cache_size > 0) {
13754       cache = (char *) M_MALLOC(cache_size);
13755       if (cache) {
13756          lseek(fhi, 0, SEEK_SET);
13757          i = read(fhi, cache, cache_size);
13758          if (i < cache_size) {
13759             M_FREE(cache);
13760             close(fh);
13761             close(fhd);
13762             close(fhi);
13763             return HS_FILE_ERROR;
13764          }
13765       }
13766 
13767       /* search record closest to start time */
13768       if (cache == NULL) {
13769          lseek(fhi, 0, SEEK_END);
13770          delta = (TELL(fhi) / sizeof(irec)) / 2;
13771          lseek(fhi, delta * sizeof(irec), SEEK_SET);
13772          do {
13773             delta = (int) (abs(delta) / 2.0 + 0.5);
13774             read(fhi, (char *) &irec, sizeof(irec));
13775             if (irec.time > start_time)
13776                delta = -delta;
13777 
13778             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
13779          } while (abs(delta) > 1 && irec.time != start_time);
13780          read(fhi, (char *) &irec, sizeof(irec));
13781          if (irec.time > start_time)
13782             delta = -abs(delta);
13783 
13784          i = TELL(fhi) + (delta - 1) * sizeof(irec);
13785          if (i <= 0)
13786             lseek(fhi, 0, SEEK_SET);
13787          else
13788             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
13789          read(fhi, (char *) &irec, sizeof(irec));
13790       } else {
13791          delta = (cache_size / sizeof(irec)) / 2;
13792          cp = delta * sizeof(irec);
13793          do {
13794             delta = (int) (abs(delta) / 2.0 + 0.5);
13795             pirec = (INDEX_RECORD *) (cache + cp);
13796             if (pirec->time > start_time)
13797                delta = -delta;
13798 
13799             cp = cp + delta * sizeof(irec);
13800          } while (abs(delta) > 1 && pirec->time != start_time);
13801          pirec = (INDEX_RECORD *) (cache + cp);
13802          if (pirec->time > start_time)
13803             delta = -abs(delta);
13804 
13805          if (cp <= delta * (int) sizeof(irec))
13806             cp = 0;
13807          else
13808             cp = cp + delta * sizeof(irec);
13809 
13810          if (cp >= cache_size)
13811             cp = cache_size - sizeof(irec);
13812          if (cp < 0)
13813             cp = 0;
13814 
13815          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
13816          cp += sizeof(irec);
13817       }
13818    } else {                     /* file size > 0 */
13819 
13820       cache = NULL;
13821       irec.time = start_time;
13822    }
13823 
13824    /* read records, skip wrong IDs */
13825    old_def_offset = -1;
13826    *n = 0;
13827    prev_time = 0;
13828    last_irec_time = 0;
13829    do {
13830       if (irec.time < last_irec_time) {
13831          cm_msg(MERROR, "hs_read",
13832                 "corrupted history data: time does not increase: %d -> %d", last_irec_time, irec.time);
13833          *tbsize = *dbsize = *n = 0;
13834          return HS_FILE_ERROR;
13835       }
13836       last_irec_time = irec.time;
13837       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
13838          /* check if record time more than "interval" seconds after previous time */
13839          if (irec.time >= prev_time + interval) {
13840             prev_time = irec.time;
13841             lseek(fh, irec.offset, SEEK_SET);
13842             read(fh, (char *) &rec, sizeof(rec));
13843 
13844             /* if definition changed, read new definition */
13845             if ((INT) rec.def_offset != old_def_offset) {
13846                lseek(fh, rec.def_offset, SEEK_SET);
13847                read(fh, (char *) &drec, sizeof(drec));
13848                read(fh, str, NAME_LENGTH);
13849 
13850                tag = (TAG *) M_MALLOC(drec.data_size);
13851                if (tag == NULL) {
13852                   *n = *tbsize = *dbsize = 0;
13853                   if (cache)
13854                      M_FREE(cache);
13855                   close(fh);
13856                   close(fhd);
13857                   close(fhi);
13858                   return HS_NO_MEMORY;
13859                }
13860                read(fh, (char *) tag, drec.data_size);
13861 
13862                /* find index of tag_name in new definition */
13863                index = -1;
13864                for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
13865                   if (equal_ustring(tag[i].name, tag_name)) {
13866                      index = i;
13867                      break;
13868                   }
13869 
13870                /*
13871                   if ((DWORD) i == drec.data_size/sizeof(TAG))
13872                   {
13873                   *n = *tbsize = *dbsize = 0;
13874                   if (cache)
13875                   M_FREE(cache);
13876 
13877                   return HS_UNDEFINED_VAR;
13878                   }
13879                 */
13880 
13881                if (index >= 0 && var_index >= tag[i].n_data) {
13882                   *n = *tbsize = *dbsize = 0;
13883                   if (cache)
13884                      M_FREE(cache);
13885                   M_FREE(tag);
13886                   close(fh);
13887                   close(fhd);
13888                   close(fhi);
13889                   return HS_WRONG_INDEX;
13890                }
13891 
13892                /* calculate offset for variable */
13893                if (index >= 0) {
13894                   *type = tag[i].type;
13895 
13896                   /* loop over all previous variables */
13897                   for (i = 0, var_offset = 0; i < index; i++)
13898                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
13899 
13900                   /* strings have size n_data */
13901                   if (tag[index].type == TID_STRING)
13902                      var_size = tag[i].n_data;
13903                   else
13904                      var_size = rpc_tid_size(tag[index].type);
13905 
13906                   var_offset += var_size * var_index;
13907                }
13908 
13909                M_FREE(tag);
13910                old_def_offset = rec.def_offset;
13911                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
13912             }
13913 
13914             if (index >= 0) {
13915                /* check if data fits in buffers */
13916                if ((*n) * sizeof(DWORD) >= *tbsize || (*n) * var_size >= *dbsize) {
13917                   *dbsize = (*n) * var_size;
13918                   *tbsize = (*n) * sizeof(DWORD);
13919                   if (cache)
13920                      M_FREE(cache);
13921                   close(fh);
13922                   close(fhd);
13923                   close(fhi);
13924                   return HS_TRUNCATED;
13925                }
13926 
13927                /* copy time from header */
13928                time_buffer[*n] = irec.time;
13929 
13930                /* copy data from record */
13931                lseek(fh, var_offset, SEEK_CUR);
13932                read(fh, (char *) data_buffer + (*n) * var_size, var_size);
13933 
13934                /* increment counter */
13935                (*n)++;
13936             }
13937          }
13938       }
13939 
13940       /* read next index record */
13941       if (cache) {
13942          if (cp >= cache_size) {
13943             i = -1;
13944             M_FREE(cache);
13945             cache = NULL;
13946          } else
13947             i = sizeof(irec);
13948 
13949          if (cp < cache_size) {
13950             memcpy(&irec, cache + cp, sizeof(irec));
13951             cp += sizeof(irec);
13952          }
13953       } else
13954          i = read(fhi, (char *) &irec, sizeof(irec));
13955 
13956       /* end of file: search next history file */
13957       if (i <= 0) {
13958          close(fh);
13959          close(fhd);
13960          close(fhi);
13961 
13962          /* advance one day */
13963          ltime = (time_t) last_irec_time;
13964          tms = localtime(&ltime);
13965          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
13966          last_irec_time = mktime(tms);
13967 
13968          last_irec_time += 3600 * 24;
13969 
13970          if (last_irec_time > end_time)
13971             break;
13972 
13973          /* search next file */
13974          status = hs_search_file(&last_irec_time, 1);
13975          if (status != HS_SUCCESS)
13976             break;
13977 
13978          /* open history and definition files */
13979          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
13980          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
13981          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
13982          if (fh < 0 || fhd < 0 || fhi < 0) {
13983             cm_msg(MERROR, "hs_read", "cannot open index files");
13984             break;
13985          }
13986 
13987          /* try to read index file into cache */
13988          lseek(fhi, 0, SEEK_END);
13989          cache_size = TELL(fhi);
13990          lseek(fhi, 0, SEEK_SET);
13991          cache = (char *) M_MALLOC(cache_size);
13992          if (cache) {
13993             i = read(fhi, cache, cache_size);
13994             if (i < cache_size)
13995                break;
13996             /* read first record */
13997             cp = 0;
13998             memcpy(&irec, cache, sizeof(irec));
13999          } else {
14000             /* read first record */
14001             i = read(fhi, (char *) &irec, sizeof(irec));
14002             if (i <= 0)
14003                break;
14004          }
14005 
14006          /* old definition becomes invalid */
14007          old_def_offset = -1;
14008       }
14009    } while (irec.time < end_time);
14010 
14011    if (cache)
14012       M_FREE(cache);
14013    close(fh);
14014    close(fhd);
14015    close(fhi);
14016 
14017    *dbsize = *n * var_size;
14018    *tbsize = *n * sizeof(DWORD);
14019 
14020    return HS_SUCCESS;
14021 }
14022 
14023 
14024 /********************************************************************/
14025 INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time, DWORD interval, BOOL binary_time)
14026 /********************************************************************\
14027 
14028   Routine: hs_dump
14029 
14030   Purpose: Display history for a given event at stdout. The output
14031            can be redirected to be read by Excel for example.
14032 
14033   Input:
14034     DWORD  event_id         Event ID
14035     DWORD  start_time       Starting Date/Time
14036     DWORD  end_time         End Date/Time
14037     DWORD  interval         Minimum time in seconds between reported
14038                             events. Can be used to skip events
14039     BOOL   binary_time      Display DWORD time stamp
14040   Output:
14041     <screen output>
14042 
14043   Function value:
14044     HS_SUCCESS              Successful completion
14045     HS_FILE_ERROR           Cannot open history file
14046 
14047 \********************************************************************/
14048 {
14049    DWORD prev_time, last_irec_time;
14050    time_t ltime;
14051    int fh, fhd, fhi;
14052    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
14053    INDEX_RECORD irec;
14054    HIST_RECORD rec, drec;
14055    INT old_def_offset, offset;
14056    TAG *tag = NULL, *old_tag = NULL;
14057    char str[NAME_LENGTH], data_buffer[10000];
14058    struct tm *tms;
14059 
14060    /* if not time given, use present to one hour in past */
14061    if (start_time == 0)
14062       start_time = time(NULL) - 3600;
14063    if (end_time == 0)
14064       end_time = time(NULL);
14065 
14066    /* search history file for start_time */
14067    status = hs_search_file(&start_time, 1);
14068    if (status != HS_SUCCESS) {
14069       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
14070       return HS_FILE_ERROR;
14071    }
14072 
14073    /* open history and definition files */
14074    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14075    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14076    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14077    if (fh < 0 || fhd < 0 || fhi < 0) {
14078       cm_msg(MERROR, "hs_dump", "cannot open index files");
14079       return HS_FILE_ERROR;
14080    }
14081 
14082    /* search record closest to start time */
14083    lseek(fhi, 0, SEEK_END);
14084    delta = (TELL(fhi) / sizeof(irec)) / 2;
14085    lseek(fhi, delta * sizeof(irec), SEEK_SET);
14086    do {
14087       delta = (int) (abs(delta) / 2.0 + 0.5);
14088       read(fhi, (char *) &irec, sizeof(irec));
14089       if (irec.time > start_time)
14090          delta = -delta;
14091 
14092       i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14093    } while (abs(delta) > 1 && irec.time != start_time);
14094    read(fhi, (char *) &irec, sizeof(irec));
14095    if (irec.time > start_time)
14096       delta = -abs(delta);
14097 
14098    i = TELL(fhi) + (delta - 1) * sizeof(irec);
14099    if (i <= 0)
14100       lseek(fhi, 0, SEEK_SET);
14101    else
14102       lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14103    read(fhi, (char *) &irec, sizeof(irec));
14104 
14105    /* read records, skip wrong IDs */
14106    old_def_offset = -1;
14107    prev_time = 0;
14108    last_irec_time = 0;
14109    do {
14110       if (irec.time < last_irec_time) {
14111          cm_msg(MERROR, "hs_dump",
14112                 "corrupted history data: time does not increase: %d -> %d", last_irec_time, irec.time);
14113          return HS_FILE_ERROR;
14114       }
14115       last_irec_time = irec.time;
14116       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14117          if (irec.time >= prev_time + interval) {
14118             prev_time = irec.time;
14119             lseek(fh, irec.offset, SEEK_SET);
14120             read(fh, (char *) &rec, sizeof(rec));
14121 
14122             /* if definition changed, read new definition */
14123             if ((INT) rec.def_offset != old_def_offset) {
14124                lseek(fh, rec.def_offset, SEEK_SET);
14125                read(fh, (char *) &drec, sizeof(drec));
14126                read(fh, str, NAME_LENGTH);
14127 
14128                if (tag == NULL)
14129                   tag = (TAG *) M_MALLOC(drec.data_size);
14130                else
14131                   tag = (TAG *) realloc(tag, drec.data_size);
14132                if (tag == NULL)
14133                   return HS_NO_MEMORY;
14134                read(fh, (char *) tag, drec.data_size);
14135                n_tag = drec.data_size / sizeof(TAG);
14136 
14137                /* print tag names if definition has changed */
14138                if (old_tag == NULL || old_n_tag != n_tag || memcmp(old_tag, tag, drec.data_size) != 0) {
14139                   printf("Date\t");
14140                   for (i = 0; i < n_tag; i++) {
14141                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
14142                         printf("%s\t", tag[i].name);
14143                      else
14144                         for (j = 0; j < (INT) tag[i].n_data; j++)
14145                            printf("%s%d\t", tag[i].name, j);
14146                   }
14147                   printf("\n");
14148 
14149                   if (old_tag == NULL)
14150                      old_tag = (TAG *) M_MALLOC(drec.data_size);
14151                   else
14152                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
14153                   memcpy(old_tag, tag, drec.data_size);
14154                   old_n_tag = n_tag;
14155                }
14156 
14157                old_def_offset = rec.def_offset;
14158                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14159             }
14160 
14161             /* print time from header */
14162             if (binary_time)
14163                printf("%d ", irec.time);
14164             else {
14165                ltime = (time_t) irec.time;
14166                sprintf(str, "%s", ctime(&ltime) + 4);
14167                str[20] = '\t';
14168                printf(str);
14169             }
14170 
14171             /* read data */
14172             read(fh, data_buffer, rec.data_size);
14173 
14174             /* interprete data from tag definition */
14175             offset = 0;
14176             for (i = 0; i < n_tag; i++) {
14177                /* strings have a length of n_data */
14178                if (tag[i].type == TID_STRING) {
14179                   printf("%s\t", data_buffer + offset);
14180                   offset += tag[i].n_data;
14181                } else if (tag[i].n_data == 1) {
14182                   /* non-array data */
14183                   db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0, tag[i].type);
14184                   printf("%s\t", str);
14185                   offset += rpc_tid_size(tag[i].type);
14186                } else
14187                   /* loop over array data */
14188                   for (j = 0; j < (INT) tag[i].n_data; j++) {
14189                      db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0, tag[i].type);
14190                      printf("%s\t", str);
14191                      offset += rpc_tid_size(tag[i].type);
14192                   }
14193             }
14194             printf("\n");
14195          }
14196       }
14197 
14198       /* read next index record */
14199       i = read(fhi, (char *) &irec, sizeof(irec));
14200 
14201       /* end of file: search next history file */
14202       if (i <= 0) {
14203          close(fh);
14204          close(fhd);
14205          close(fhi);
14206 
14207          /* advance one day */
14208          ltime = (time_t) last_irec_time;
14209          tms = localtime(&ltime);
14210          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14211          last_irec_time = mktime(tms);
14212 
14213          last_irec_time += 3600 * 24;
14214          if (last_irec_time > end_time)
14215             break;
14216 
14217          /* search next file */
14218          status = hs_search_file((DWORD *) & last_irec_time, 1);
14219          if (status != HS_SUCCESS)
14220             break;
14221 
14222          /* open history and definition files */
14223          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14224          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14225          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14226          if (fh < 0 || fhd < 0 || fhi < 0) {
14227             cm_msg(MERROR, "hs_dump", "cannot open index files");
14228             break;
14229          }
14230 
14231          /* read first record */
14232          i = read(fhi, (char *) &irec, sizeof(irec));
14233          if (i <= 0)
14234             break;
14235 
14236          /* old definition becomes invalid */
14237          old_def_offset = -1;
14238       }
14239    } while (irec.time < end_time);
14240 
14241    M_FREE(tag);
14242    M_FREE(old_tag);
14243    close(fh);
14244    close(fhd);
14245    close(fhi);
14246 
14247    return HS_SUCCESS;
14248 }
14249 
14250 
14251 /********************************************************************/
14252 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
14253 /********************************************************************\
14254 
14255   Routine: hs_fdump
14256 
14257   Purpose: Display history for a given history file
14258 
14259   Input:
14260     char   *file_name       Name of file to dump
14261     DWORD  event_id         Event ID
14262     BOOL   binary_time      Display DWORD time stamp
14263 
14264   Output:
14265     <screen output>
14266 
14267   Function value:
14268     HS_SUCCESS              Successful completion
14269     HS_FILE_ERROR           Cannot open history file
14270 
14271 \********************************************************************/
14272 {
14273    int fh;
14274    INT n;
14275    time_t ltime;
14276    HIST_RECORD rec;
14277    char event_name[NAME_LENGTH];
14278    char str[256];
14279 
14280    /* open file, add O_BINARY flag for Windows NT */
14281    sprintf(str, "%s%s", _hs_path_name, file_name);
14282    fh = open(str, O_RDONLY | O_BINARY, 0644);
14283    if (fh < 0) {
14284       cm_msg(MERROR, "hs_fdump", "cannot open file %s", str);
14285       return HS_FILE_ERROR;
14286    }
14287 
14288    /* loop over file records in .hst file */
14289    do {
14290       n = read(fh, (char *) &rec, sizeof(rec));
14291       if (n < sizeof(rec))
14292          break;
14293 
14294       /* check if record type is definition */
14295       if (rec.record_type == RT_DEF) {
14296          /* read name */
14297          read(fh, event_name, sizeof(event_name));
14298 
14299          if (rec.event_id == id || id == 0)
14300             printf("Event definition %s, ID %d\n", event_name, rec.event_id);
14301 
14302          /* skip tags */
14303          lseek(fh, rec.data_size, SEEK_CUR);
14304       } else {
14305          /* print data record */
14306          if (binary_time)
14307             sprintf(str, "%d ", rec.time);
14308          else {
14309             ltime = (time_t) rec.time;
14310             strcpy(str, ctime(&ltime) + 4);
14311             str[15] = 0;
14312          }
14313          if (rec.event_id == id || id == 0)
14314             printf("ID %d, %s, size %d\n", rec.event_id, str, rec.data_size);
14315 
14316          /* skip data */
14317          lseek(fh, rec.data_size, SEEK_CUR);
14318       }
14319 
14320    } while (TRUE);
14321 
14322    close(fh);
14323 
14324    return HS_SUCCESS;
14325 }
14326 #endif                          /* OS_VXWORKS hs section */
14327 
14328 /**dox***************************************************************/
14329 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14330 
14331 /**dox***************************************************************/
14332                                                                                                              /** @} *//* end of hsfunctionc */
14333 
14334 /**dox***************************************************************/
14335 /** @addtogroup elfunctionc
14336  *
14337  *  @{  */
14338 
14339 /**dox***************************************************************/
14340 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14341 
14342 /********************************************************************\
14343 *                                                                    *
14344 *               Electronic logbook functions                         *
14345 *                                                                    *
14346 \********************************************************************/
14347 
14348 /********************************************************************/
14349 void el_decode(char *message, char *key, char *result, int size)
14350 {
14351    char *rstart = result;
14352    char *pc;
14353 
14354    if (result == NULL)
14355       return;
14356 
14357    *result = 0;
14358 
14359    if (strstr(message, key)) {
14360       for (pc = strstr(message, key) + strlen(key); *pc != '\n';)
14361          *result++ = *pc++;
14362       *result = 0;
14363    }
14364 
14365    assert((int) strlen(rstart) < size);
14366 }
14367 
14368 /**dox***************************************************************/
14369 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14370 
14371 /********************************************************************/
14372 /**
14373 Submit an ELog entry.
14374 @param run  Run Number.
14375 @param author Message author.
14376 @param type Message type.
14377 @param system Message system.
14378 @param subject Subject.
14379 @param text Message text.
14380 @param reply_to In reply to this message.
14381 @param encoding Text encoding, either HTML or plain.
14382 @param afilename1   File name of attachment.
14383 @param buffer1      File contents.
14384 @param buffer_size1 Size of buffer in bytes.
14385 @param afilename2   File name of attachment.
14386 @param buffer2      File contents.
14387 @param buffer_size2 Size of buffer in bytes.
14388 @param afilename3   File name of attachment.
14389 @param buffer3      File contents.
14390 @param buffer_size3 Size of buffer in bytes.
14391 @param tag          If given, edit existing message.
14392 @param tag_size     Maximum size of tag.
14393 @return EL_SUCCESS
14394 */
14395 INT el_submit(int run, char *author, char *type, char *system, char *subject,
14396               char *text, char *reply_to, char *encoding,
14397               char *afilename1, char *buffer1, INT buffer_size1,
14398               char *afilename2, char *buffer2, INT buffer_size2,
14399               char *afilename3, char *buffer3, INT buffer_size3, char *tag, INT tag_size)
14400 {
14401    if (rpc_is_remote())
14402       return rpc_call(RPC_EL_SUBMIT, run, author, type, system, subject,
14403                       text, reply_to, encoding,
14404                       afilename1, buffer1, buffer_size1,
14405                       afilename2, buffer2, buffer_size2, afilename3, buffer3, buffer_size3, tag, tag_size);
14406 
14407 #ifdef LOCAL_ROUTINES
14408    {
14409       INT n, size, fh, status, run_number, mutex, buffer_size = 0, index, offset = 0, tail_size = 0;
14410       struct tm *tms = NULL;
14411       char afilename[256], file_name[256], afile_name[3][256], dir[256], str[256],
14412           start_str[80], end_str[80], last[80], date[80], thread[80], attachment[256];
14413       HNDLE hDB;
14414       time_t now;
14415       char message[10000], *p, *buffer = NULL;
14416       BOOL bedit;
14417 
14418       cm_get_experiment_database(&hDB, NULL);
14419 
14420       bedit = (tag[0] != 0);
14421 
14422       /* request semaphore */
14423       cm_get_experiment_mutex(NULL, &mutex);
14424       status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
14425       if (status != SS_SUCCESS) {
14426          cm_msg(MERROR, "el_submit", "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
14427          abort();
14428       }
14429 
14430       /* get run number from ODB if not given */
14431       if (run > 0)
14432          run_number = run;
14433       else {
14434          /* get run number */
14435          size = sizeof(run_number);
14436          status = db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
14437          assert(status == SUCCESS);
14438       }
14439 
14440       if (run_number < 0) {
14441          cm_msg(MERROR, "el_submit", "aborting on attempt to use invalid run number %d", run_number);
14442          abort();
14443       }
14444 
14445       for (index = 0; index < 3; index++) {
14446          /* generate filename for attachment */
14447          afile_name[index][0] = file_name[0] = 0;
14448 
14449          if (index == 0) {
14450             strcpy(afilename, afilename1);
14451             buffer = buffer1;
14452             buffer_size = buffer_size1;
14453          } else if (index == 1) {
14454             strcpy(afilename, afilename2);
14455             buffer = buffer2;
14456             buffer_size = buffer_size2;
14457          } else if (index == 2) {
14458             strcpy(afilename, afilename3);
14459             buffer = buffer3;
14460             buffer_size = buffer_size3;
14461          }
14462 
14463          if (afilename[0]) {
14464             strcpy(file_name, afilename);
14465             p = file_name;
14466             while (strchr(p, ':'))
14467                p = strchr(p, ':') + 1;
14468             while (strchr(p, '\\'))
14469                p = strchr(p, '\\') + 1; /* NT */
14470             while (strchr(p, '/'))
14471                p = strchr(p, '/') + 1;  /* Unix */
14472             while (strchr(p, ']'))
14473                p = strchr(p, ']') + 1;  /* VMS */
14474 
14475             /* assemble ELog filename */
14476             if (p[0]) {
14477                dir[0] = 0;
14478                if (hDB > 0) {
14479                   size = sizeof(dir);
14480                   memset(dir, 0, size);
14481                   status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
14482                   if (status != DB_SUCCESS)
14483                      db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
14484 
14485                   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14486                      strcat(dir, DIR_SEPARATOR_STR);
14487                }
14488 #if !defined(OS_VXWORKS)
14489 #if !defined(OS_VMS)
14490                tzset();
14491 #endif
14492 #endif
14493 
14494                time(&now);
14495                tms = localtime(&now);
14496 
14497                strcpy(str, p);
14498                sprintf(afile_name[index], "%02d%02d%02d_%02d%02d%02d_%s",
14499                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14500                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14501                sprintf(file_name, "%s%02d%02d%02d_%02d%02d%02d_%s", dir,
14502                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14503                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14504 
14505                /* save attachment */
14506                fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14507                if (fh < 0) {
14508                   cm_msg(MERROR, "el_submit", "Cannot write attachment file \"%s\"", file_name);
14509                } else {
14510                   write(fh, buffer, buffer_size);
14511                   close(fh);
14512                }
14513             }
14514          }
14515       }
14516 
14517       /* generate new file name YYMMDD.log in data directory */
14518       cm_get_experiment_database(&hDB, NULL);
14519 
14520       size = sizeof(dir);
14521       memset(dir, 0, size);
14522       status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
14523       if (status != DB_SUCCESS)
14524          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
14525 
14526       if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14527          strcat(dir, DIR_SEPARATOR_STR);
14528 
14529 #if !defined(OS_VXWORKS)
14530 #if !defined(OS_VMS)
14531       tzset();
14532 #endif
14533 #endif
14534 
14535       if (bedit) {
14536          /* edit existing message */
14537          strcpy(str, tag);
14538          if (strchr(str, '.')) {
14539             offset = atoi(strchr(str, '.') + 1);
14540             *strchr(str, '.') = 0;
14541          }
14542          sprintf(file_name, "%s%s.log", dir, str);
14543          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14544          if (fh < 0) {
14545             ss_mutex_release(mutex);
14546             return EL_FILE_ERROR;
14547          }
14548          lseek(fh, offset, SEEK_SET);
14549          read(fh, str, 16);
14550          assert(strncmp(str, "$Start$", 7) == 0);
14551 
14552          size = atoi(str + 9);
14553          read(fh, message, size);
14554 
14555          el_decode(message, "Date: ", date, sizeof(date));
14556          el_decode(message, "Thread: ", thread, sizeof(thread));
14557          el_decode(message, "Attachment: ", attachment, sizeof(attachment));
14558 
14559          /* buffer tail of logfile */
14560          lseek(fh, 0, SEEK_END);
14561          tail_size = TELL(fh) - (offset + size);
14562 
14563          if (tail_size > 0) {
14564             buffer = (char *) M_MALLOC(tail_size);
14565             if (buffer == NULL) {
14566                close(fh);
14567                ss_mutex_release(mutex);
14568                return EL_FILE_ERROR;
14569             }
14570 
14571             lseek(fh, offset + size, SEEK_SET);
14572             n = read(fh, buffer, tail_size);
14573             assert(n == tail_size);
14574          }
14575          lseek(fh, offset, SEEK_SET);
14576       } else {
14577          /* create new message */
14578          time(&now);
14579          tms = localtime(&now);
14580 
14581          sprintf(file_name, "%s%02d%02d%02d.log", dir, tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14582 
14583          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14584          if (fh < 0) {
14585             ss_mutex_release(mutex);
14586             return EL_FILE_ERROR;
14587          }
14588 
14589          strcpy(date, ctime(&now));
14590          date[24] = 0;
14591 
14592          if (reply_to[0])
14593             sprintf(thread, "%16s %16s", reply_to, "0");
14594          else
14595             sprintf(thread, "%16s %16s", "0", "0");
14596 
14597          lseek(fh, 0, SEEK_END);
14598       }
14599 
14600       /* compose message */
14601 
14602       sprintf(message, "Date: %s\n", date);
14603       sprintf(message + strlen(message), "Thread: %s\n", thread);
14604       sprintf(message + strlen(message), "Run: %d\n", run_number);
14605       sprintf(message + strlen(message), "Author: %s\n", author);
14606       sprintf(message + strlen(message), "Type: %s\n", type);
14607       sprintf(message + strlen(message), "System: %s\n", system);
14608       sprintf(message + strlen(message), "Subject: %s\n", subject);
14609 
14610       /* keep original attachment if edit and no new attachment */
14611       if (bedit && afile_name[0][0] == 0 && afile_name[1][0] == 0 && afile_name[2][0] == 0)
14612          sprintf(message + strlen(message), "Attachment: %s", attachment);
14613       else {
14614          sprintf(message + strlen(message), "Attachment: %s", afile_name[0]);
14615          if (afile_name[1][0])
14616             sprintf(message + strlen(message), ",%s", afile_name[1]);
14617          if (afile_name[2][0])
14618             sprintf(message + strlen(message), ",%s", afile_name[2]);
14619       }
14620       sprintf(message + strlen(message), "\n");
14621 
14622       sprintf(message + strlen(message), "Encoding: %s\n", encoding);
14623       sprintf(message + strlen(message), "========================================\n");
14624       strcat(message, text);
14625 
14626       assert(strlen(message) < sizeof(message));        /* bomb out on array overrun. */
14627 
14628       size = 0;
14629       sprintf(start_str, "$Start$: %6d\n", size);
14630       sprintf(end_str, "$End$:   %6d\n\f", size);
14631 
14632       size = strlen(message) + strlen(start_str) + strlen(end_str);
14633 
14634       if (tag != NULL && !bedit)
14635          sprintf(tag, "%02d%02d%02d.%d", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, (int) TELL(fh));
14636 
14637       /* size has to fit in 6 digits */
14638       assert(size < 999999);
14639 
14640       sprintf(start_str, "$Start$: %6d\n", size);
14641       sprintf(end_str, "$End$:   %6d\n\f", size);
14642 
14643       write(fh, start_str, strlen(start_str));
14644       write(fh, message, strlen(message));
14645       write(fh, end_str, strlen(end_str));
14646 
14647       if (bedit) {
14648          if (tail_size > 0) {
14649             n = write(fh, buffer, tail_size);
14650             M_FREE(buffer);
14651          }
14652 
14653          /* truncate file here */
14654 #ifdef OS_WINNT
14655          chsize(fh, TELL(fh));
14656 #else
14657          ftruncate(fh, TELL(fh));
14658 #endif
14659       }
14660 
14661       close(fh);
14662 
14663       /* if reply, mark original message */
14664       if (reply_to[0] && !bedit) {
14665          strcpy(last, reply_to);
14666          do {
14667             status = el_search_message(last, &fh, FALSE);
14668             if (status == EL_SUCCESS) {
14669                /* position to next thread location */
14670                lseek(fh, 72, SEEK_CUR);
14671                memset(str, 0, sizeof(str));
14672                read(fh, str, 16);
14673                lseek(fh, -16, SEEK_CUR);
14674 
14675                /* if no reply yet, set it */
14676                if (atoi(str) == 0) {
14677                   sprintf(str, "%16s", tag);
14678                   write(fh, str, 16);
14679                   close(fh);
14680                   break;
14681                } else {
14682                   /* if reply set, find last one in chain */
14683                   strcpy(last, strtok(str, " "));
14684                   close(fh);
14685                }
14686             } else
14687                /* stop on error */
14688                break;
14689 
14690          } while (TRUE);
14691       }
14692 
14693       /* release elog mutex */
14694       ss_mutex_release(mutex);
14695    }
14696 #endif                          /* LOCAL_ROUTINES */
14697 
14698    return EL_SUCCESS;
14699 }
14700 
14701 /**dox***************************************************************/
14702 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14703 
14704 /********************************************************************/
14705 INT el_search_message(char *tag, int *fh, BOOL walk)
14706 {
14707    int i, size, offset, direction, last, status;
14708    struct tm *tms, ltms;
14709    time_t lt, ltime, lact;
14710    char str[256], file_name[256], dir[256];
14711    HNDLE hDB;
14712 
14713 #if !defined(OS_VXWORKS)
14714 #if !defined(OS_VMS)
14715    tzset();
14716 #endif
14717 #endif
14718 
14719    /* open file */
14720    cm_get_experiment_database(&hDB, NULL);
14721 
14722    size = sizeof(dir);
14723    memset(dir, 0, size);
14724    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
14725    if (status != DB_SUCCESS)
14726       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
14727 
14728    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14729       strcat(dir, DIR_SEPARATOR_STR);
14730 
14731    /* check tag for direction */
14732    direction = 0;
14733    if (strpbrk(tag, "+-")) {
14734       direction = atoi(strpbrk(tag, "+-"));
14735       *strpbrk(tag, "+-") = 0;
14736    }
14737 
14738    /* if tag is given, open file directly */
14739    if (tag[0]) {
14740       /* extract time structure from tag */
14741       tms = &ltms;
14742       memset(tms, 0, sizeof(struct tm));
14743       tms->tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
14744       tms->tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
14745       tms->tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
14746       tms->tm_hour = 12;
14747 
14748       if (tms->tm_year < 90)
14749          tms->tm_year += 100;
14750       ltime = lt = mktime(tms);
14751 
14752       strcpy(str, tag);
14753       if (strchr(str, '.')) {
14754          offset = atoi(strchr(str, '.') + 1);
14755          *strchr(str, '.') = 0;
14756       } else
14757          return EL_FILE_ERROR;
14758 
14759       do {
14760          tms = localtime(&ltime);
14761 
14762          sprintf(file_name, "%s%02d%02d%02d.log", dir, tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14763          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
14764 
14765          if (*fh < 0) {
14766             if (!walk)
14767                return EL_FILE_ERROR;
14768 
14769             if (direction == -1)
14770                ltime -= 3600 * 24;      /* one day back */
14771             else
14772                ltime += 3600 * 24;      /* go forward one day */
14773 
14774             /* set new tag */
14775             tms = localtime(&ltime);
14776             sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14777          }
14778 
14779          /* in forward direction, stop today */
14780          if (direction != -1 && ltime > time(NULL) + 3600 * 24)
14781             break;
14782 
14783          /* in backward direction, go back 10 years */
14784          if (direction == -1 && abs((INT) lt - (INT) ltime) > 3600 * 24 * 365 * 10)
14785             break;
14786 
14787       } while (*fh < 0);
14788 
14789       if (*fh < 0)
14790          return EL_FILE_ERROR;
14791 
14792       lseek(*fh, offset, SEEK_SET);
14793 
14794       /* check if start of message */
14795       i = read(*fh, str, 15);
14796       if (i <= 0) {
14797          close(*fh);
14798          return EL_FILE_ERROR;
14799       }
14800 
14801       if (strncmp(str, "$Start$: ", 9) != 0) {
14802          close(*fh);
14803          return EL_FILE_ERROR;
14804       }
14805 
14806       lseek(*fh, offset, SEEK_SET);
14807    }
14808 
14809    /* open most recent file if no tag given */
14810    if (tag[0] == 0) {
14811       time((long *) &lt);
14812       ltime = lt;
14813       do {
14814          tms = localtime(&ltime);
14815 
14816          sprintf(file_name, "%s%02d%02d%02d.log", dir, tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14817          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
14818 
14819          if (*fh < 0)
14820             ltime -= 3600 * 24; /* one day back */
14821 
14822       } while (*fh < 0 && (INT) lt - (INT) ltime < 3600 * 24 * 365);
14823 
14824       if (*fh < 0)
14825          return EL_FILE_ERROR;
14826 
14827       /* remember tag */
14828       sprintf(tag, "%02d%02d%02d", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14829 
14830       lseek(*fh, 0, SEEK_END);
14831 
14832       sprintf(tag + strlen(tag), ".%d", (int) TELL(*fh));
14833    }
14834 
14835 
14836    if (direction == -1) {
14837       /* seek previous message */
14838 
14839       if (TELL(*fh) == 0) {
14840          /* go back one day */
14841          close(*fh);
14842 
14843          lt = ltime;
14844          do {
14845             lt -= 3600 * 24;
14846             tms = localtime(&lt);
14847             sprintf(str, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14848 
14849             status = el_search_message(str, fh, FALSE);
14850 
14851          } while (status != EL_SUCCESS && (INT) ltime - (INT) lt < 3600 * 24 * 365);
14852 
14853          if (status != EL_SUCCESS)
14854             return EL_FIRST_MSG;
14855 
14856          /* adjust tag */
14857          strcpy(tag, str);
14858 
14859          /* go to end of current file */
14860          lseek(*fh, 0, SEEK_END);
14861       }
14862 
14863       /* read previous message size */
14864       lseek(*fh, -17, SEEK_CUR);
14865       i = read(*fh, str, 17);
14866       if (i <= 0) {
14867          close(*fh);
14868          return EL_FILE_ERROR;
14869       }
14870 
14871       if (strncmp(str, "$End$: ", 7) != 0) {
14872          close(*fh);
14873          return EL_FILE_ERROR;
14874       }
14875 
14876       /* make sure the input string to atoi() is zero-terminated:
14877        * $End$:      355garbage
14878        * 01234567890123456789 */
14879       str[15] = 0;
14880 
14881       size = atoi(str + 7);
14882       assert(size > 15);
14883 
14884       lseek(*fh, -size, SEEK_CUR);
14885 
14886       /* adjust tag */
14887       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
14888    }
14889 
14890    if (direction == 1) {
14891       /* seek next message */
14892 
14893       /* read current message size */
14894       last = TELL(*fh);
14895 
14896       i = read(*fh, str, 15);
14897       if (i <= 0) {
14898          close(*fh);
14899          return EL_FILE_ERROR;
14900       }
14901       lseek(*fh, -15, SEEK_CUR);
14902 
14903       if (strncmp(str, "$Start$: ", 9) != 0) {
14904          close(*fh);
14905          return EL_FILE_ERROR;
14906       }
14907 
14908       /* make sure the input string to atoi() is zero-terminated
14909        * $Start$:    606garbage
14910        * 01234567890123456789 */
14911       str[15] = 0;
14912 
14913       size = atoi(str + 9);
14914       assert(size > 15);
14915 
14916       lseek(*fh, size, SEEK_CUR);
14917 
14918       /* if EOF, goto next day */
14919       i = read(*fh, str, 15);
14920       if (i < 15) {
14921          close(*fh);
14922          time((long *) &lact);
14923 
14924          lt = ltime;
14925          do {
14926             lt += 3600 * 24;
14927             tms = localtime(&lt);
14928             sprintf(str, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14929 
14930             status = el_search_message(str, fh, FALSE);
14931 
14932          } while (status != EL_SUCCESS && (INT) lt - (INT) lact < 3600 * 24);
14933 
14934          if (status != EL_SUCCESS)
14935             return EL_LAST_MSG;
14936 
14937          /* adjust tag */
14938          strcpy(tag, str);
14939 
14940          /* go to beginning of current file */
14941          lseek(*fh, 0, SEEK_SET);
14942       } else
14943          lseek(*fh, -15, SEEK_CUR);
14944 
14945       /* adjust tag */
14946       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
14947    }
14948 
14949    return EL_SUCCESS;
14950 }
14951 
14952 
14953 /********************************************************************/
14954 INT el_retrieve(char *tag, char *date, int *run, char *author, char *type,
14955                 char *system, char *subject, char *text, int *textsize,
14956                 char *orig_tag, char *reply_tag,
14957                 char *attachment1, char *attachment2, char *attachment3, char *encoding)
14958 /********************************************************************\
14959 
14960   Routine: el_retrieve
14961 
14962   Purpose: Retrieve an ELog entry by its message tab
14963 
14964   Input:
14965     char   *tag             tag in the form YYMMDD.offset
14966     int    *size            Size of text buffer
14967 
14968   Output:
14969     char   *tag             tag of retrieved message
14970     char   *date            Date/time of message recording
14971     int    *run             Run number
14972     char   *author          Message author
14973     char   *type            Message type
14974     char   *system          Message system
14975     char   *subject         Subject
14976     char   *text            Message text
14977     char   *orig_tag        Original message if this one is a reply
14978     char   *reply_tag       Reply for current message
14979     char   *attachment1/2/3 File attachment
14980     char   *encoding        Encoding of message
14981     int    *size            Actual message text size
14982 
14983   Function value:
14984     EL_SUCCESS              Successful completion
14985     EL_LAST_MSG             Last message in log
14986 
14987 \********************************************************************/
14988 {
14989    int size, fh = 0, offset, search_status, rd;
14990    char str[256], *p;
14991    char message[10000], thread[256], attachment_all[256];
14992 
14993    if (tag[0]) {
14994       search_status = el_search_message(tag, &fh, TRUE);
14995       if (search_status != EL_SUCCESS)
14996          return search_status;
14997    } else {
14998       /* open most recent message */
14999       strcpy(tag, "-1");
15000       search_status = el_search_message(tag, &fh, TRUE);
15001       if (search_status != EL_SUCCESS)
15002          return search_status;
15003    }
15004 
15005    /* extract message size */
15006    offset = TELL(fh);
15007    rd = read(fh, str, 15);
15008    assert(rd == 15);
15009 
15010    /* make sure the input string is zero-terminated before we call atoi() */
15011    str[15] = 0;
15012 
15013    /* get size */
15014    size = atoi(str + 9);
15015 
15016    assert(strncmp(str, "$Start$:", 8) == 0);
15017    assert(size > 15);
15018    assert(size < sizeof(message));
15019 
15020    memset(message, 0, sizeof(message));
15021 
15022    rd = read(fh, message, size);
15023    assert(rd > 0);
15024    assert((rd + 15 == size) || (rd == size));
15025 
15026    close(fh);
15027 
15028    /* decode message */
15029    if (strstr(message, "Run: ") && run)
15030       *run = atoi(strstr(message, "Run: ") + 5);
15031 
15032    el_decode(message, "Date: ", date, 80);      /* size from show_elog_submit_query() */
15033    el_decode(message, "Thread: ", thread, sizeof(thread));
15034    el_decode(message, "Author: ", author, 80);  /* size from show_elog_submit_query() */
15035    el_decode(message, "Type: ", type, 80);      /* size from show_elog_submit_query() */
15036    el_decode(message, "System: ", system, 80);  /* size from show_elog_submit_query() */
15037    el_decode(message, "Subject: ", subject, 256);       /* size from show_elog_submit_query() */
15038    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
15039    el_decode(message, "Encoding: ", encoding, 80);      /* size from show_elog_submit_query() */
15040 
15041    /* break apart attachements */
15042    if (attachment1 && attachment2 && attachment3) {
15043       attachment1[0] = attachment2[0] = attachment3[0] = 0;
15044       p = strtok(attachment_all, ",");
15045       if (p != NULL) {
15046          strcpy(attachment1, p);
15047          p = strtok(NULL, ",");
15048          if (p != NULL) {
15049             strcpy(attachment2, p);
15050             p = strtok(NULL, ",");
15051             if (p != NULL)
15052                strcpy(attachment3, p);
15053          }
15054       }
15055 
15056       assert(strlen(attachment1) < 256);        /* size from show_elog_submit_query() */
15057       assert(strlen(attachment2) < 256);        /* size from show_elog_submit_query() */
15058       assert(strlen(attachment3) < 256);        /* size from show_elog_submit_query() */
15059    }
15060 
15061    /* conver thread in reply-to and reply-from */
15062    if (orig_tag != NULL && reply_tag != NULL) {
15063       p = strtok(thread, " \r");
15064       if (p != NULL)
15065          strcpy(orig_tag, p);
15066       else
15067          strcpy(orig_tag, "");
15068       p = strtok(NULL, " \r");
15069       if (p != NULL)
15070          strcpy(reply_tag, p);
15071       else
15072          strcpy(reply_tag, "");
15073       if (atoi(orig_tag) == 0)
15074          orig_tag[0] = 0;
15075       if (atoi(reply_tag) == 0)
15076          reply_tag[0] = 0;
15077    }
15078 
15079    p = strstr(message, "========================================\n");
15080 
15081    if (text != NULL) {
15082       if (p != NULL) {
15083          p += 41;
15084          if ((int) strlen(p) >= *textsize) {
15085             strncpy(text, p, *textsize - 1);
15086             text[*textsize - 1] = 0;
15087             return EL_TRUNCATED;
15088          } else {
15089             strcpy(text, p);
15090 
15091             /* strip end tag */
15092             if (strstr(text, "$End$"))
15093                *strstr(text, "$End$") = 0;
15094 
15095             *textsize = strlen(text);
15096          }
15097       } else {
15098          text[0] = 0;
15099          *textsize = 0;
15100       }
15101    }
15102 
15103    if (search_status == EL_LAST_MSG)
15104       return EL_LAST_MSG;
15105 
15106    return EL_SUCCESS;
15107 }
15108 
15109 
15110 /********************************************************************/
15111 INT el_search_run(int run, char *return_tag)
15112 /********************************************************************\
15113 
15114   Routine: el_search_run
15115 
15116   Purpose: Find first message belonging to a specific run
15117 
15118   Input:
15119     int    run              Run number
15120 
15121   Output:
15122     char   *tag             tag of retrieved message
15123 
15124   Function value:
15125     EL_SUCCESS              Successful completion
15126     EL_LAST_MSG             Last message in log
15127 
15128 \********************************************************************/
15129 {
15130    int actual_run, fh, status;
15131    char tag[256];
15132 
15133    tag[0] = return_tag[0] = 0;
15134 
15135    do {
15136       /* open first message in file */
15137       strcat(tag, "-1");
15138       status = el_search_message(tag, &fh, TRUE);
15139       if (status == EL_FIRST_MSG)
15140          break;
15141       if (status != EL_SUCCESS)
15142          return status;
15143       close(fh);
15144 
15145       if (strchr(tag, '.') != NULL)
15146          strcpy(strchr(tag, '.'), ".0");
15147 
15148       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15149                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15150    } while (actual_run >= run);
15151 
15152    while (actual_run < run) {
15153       strcat(tag, "+1");
15154       status = el_search_message(tag, &fh, TRUE);
15155       if (status == EL_LAST_MSG)
15156          break;
15157       if (status != EL_SUCCESS)
15158          return status;
15159       close(fh);
15160 
15161       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15162                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15163    }
15164 
15165    strcpy(return_tag, tag);
15166 
15167    if (status == EL_LAST_MSG || status == EL_FIRST_MSG)
15168       return status;
15169 
15170    return EL_SUCCESS;
15171 }
15172 
15173 
15174 /********************************************************************/
15175 INT el_delete_message(char *tag)
15176 /********************************************************************\
15177 
15178   Routine: el_submit
15179 
15180   Purpose: Submit an ELog entry
15181 
15182   Input:
15183     char   *tag             Message tage
15184 
15185   Output:
15186     <none>
15187 
15188   Function value:
15189     EL_SUCCESS              Successful completion
15190 
15191 \********************************************************************/
15192 {
15193 #ifdef LOCAL_ROUTINES
15194    INT n, size, fh, mutex, offset = 0, tail_size, status;
15195    char dir[256], str[256], file_name[256];
15196    HNDLE hDB;
15197    char *buffer = NULL;
15198 
15199    cm_get_experiment_database(&hDB, NULL);
15200 
15201    /* request semaphore */
15202    cm_get_experiment_mutex(NULL, &mutex);
15203    status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
15204    if (status != SS_SUCCESS) {
15205       cm_msg(MERROR, "el_delete_message",
15206              "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
15207       abort();
15208    }
15209 
15210 
15211    /* generate file name YYMMDD.log in data directory */
15212    cm_get_experiment_database(&hDB, NULL);
15213 
15214    size = sizeof(dir);
15215    memset(dir, 0, size);
15216    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15217    if (status != DB_SUCCESS)
15218       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15219 
15220    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15221       strcat(dir, DIR_SEPARATOR_STR);
15222 
15223    strcpy(str, tag);
15224    if (strchr(str, '.')) {
15225       offset = atoi(strchr(str, '.') + 1);
15226       *strchr(str, '.') = 0;
15227    }
15228    sprintf(file_name, "%s%s.log", dir, str);
15229    fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15230    if (fh < 0) {
15231       ss_mutex_release(mutex);
15232       return EL_FILE_ERROR;
15233    }
15234    lseek(fh, offset, SEEK_SET);
15235    read(fh, str, 16);
15236    size = atoi(str + 9);
15237 
15238    /* buffer tail of logfile */
15239    lseek(fh, 0, SEEK_END);
15240    tail_size = TELL(fh) - (offset + size);
15241 
15242    if (tail_size > 0) {
15243       buffer = (char *) M_MALLOC(tail_size);
15244       if (buffer == NULL) {
15245          close(fh);
15246          ss_mutex_release(mutex);
15247          return EL_FILE_ERROR;
15248       }
15249 
15250       lseek(fh, offset + size, SEEK_SET);
15251       n = read(fh, buffer, tail_size);
15252    }
15253    lseek(fh, offset, SEEK_SET);
15254 
15255    if (tail_size > 0) {
15256       n = write(fh, buffer, tail_size);
15257       M_FREE(buffer);
15258    }
15259 
15260    /* truncate file here */
15261 #ifdef OS_WINNT
15262    chsize(fh, TELL(fh));
15263 #else
15264    ftruncate(fh, TELL(fh));
15265 #endif
15266 
15267    /* if file length gets zero, delete file */
15268    tail_size = lseek(fh, 0, SEEK_END);
15269    close(fh);
15270 
15271    if (tail_size == 0)
15272       remove(file_name);
15273 
15274    /* release elog mutex */
15275    ss_mutex_release(mutex);
15276 #endif                          /* LOCAL_ROUTINES */
15277 
15278    return EL_SUCCESS;
15279 }
15280 
15281 /**dox***************************************************************/
15282 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15283 
15284 /**dox***************************************************************/
15285                                                                                                              /** @} *//* end of elfunctionc */
15286 
15287 /**dox***************************************************************/
15288 /** @addtogroup alfunctionc
15289  *
15290  *  @{  */
15291 
15292 /**dox***************************************************************/
15293 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15294 
15295 /********************************************************************\
15296 *                                                                    *
15297 *                     Alarm functions                                *
15298 *                                                                    *
15299 \********************************************************************/
15300 
15301 /********************************************************************/
15302 BOOL al_evaluate_condition(char *condition, char *value)
15303 {
15304    HNDLE hDB, hkey;
15305    int i, j, index, size;
15306    KEY key;
15307    double value1, value2;
15308    char value1_str[256], value2_str[256], str[256], op[3], function[80];
15309    char data[10000];
15310    DWORD dtime;
15311 
15312    strcpy(str, condition);
15313    op[1] = op[2] = 0;
15314    value1 = value2 = 0;
15315    index = 0;
15316 
15317    /* find value and operator */
15318    for (i = strlen(str) - 1; i > 0; i--)
15319       if (strchr("<>=!", str[i]) != NULL)
15320          break;
15321    op[0] = str[i];
15322    for (j = 1; str[i + j] == ' '; j++);
15323    strlcpy(value2_str, str + i + j, sizeof(value2_str));
15324    value2 = atof(value2_str);
15325    str[i] = 0;
15326 
15327    if (i > 0 && strchr("<>=!", str[i - 1])) {
15328       op[1] = op[0];
15329       op[0] = str[--i];
15330       str[i] = 0;
15331    }
15332 
15333    i--;
15334    while (i > 0 && str[i] == ' ')
15335       i--;
15336    str[i + 1] = 0;
15337 
15338    /* check if function */
15339    function[0] = 0;
15340    if (str[i] == ')') {
15341       str[i--] = 0;
15342       if (strchr(str, '(')) {
15343          *strchr(str, '(') = 0;
15344          strcpy(function, str);
15345          for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
15346             str[j] = str[i];
15347          str[j] = 0;
15348          i = j - 1;
15349       }
15350    }
15351 
15352    /* find key */
15353    if (str[i] == ']') {
15354       str[i--] = 0;
15355       while (i > 0 && isdigit(str[i]))
15356          i--;
15357       index = atoi(str + i + 1);
15358       str[i] = 0;
15359    }
15360 
15361    cm_get_experiment_database(&hDB, NULL);
15362    db_find_key(hDB, 0, str, &hkey);
15363    if (!hkey) {
15364       cm_msg(MERROR, "al_evaluate_condition", "Cannot find key %s to evaluate alarm condition", str);
15365       if (value)
15366          strcpy(value, "unknown");
15367       return FALSE;
15368    }
15369 
15370    if (equal_ustring(function, "access")) {
15371       /* check key access time */
15372       db_get_key_time(hDB, hkey, &dtime);
15373       sprintf(value1_str, "%d", dtime);
15374       value1 = atof(value1_str);
15375    } else {
15376       /* get key data and convert to double */
15377       db_get_key(hDB, hkey, &key);
15378       size = sizeof(data);
15379       db_get_data(hDB, hkey, data, &size, key.type);
15380       db_sprintf(value1_str, data, size, index, key.type);
15381       value1 = atof(value1_str);
15382    }
15383 
15384    /* convert boolean values to integers */
15385    if (key.type == TID_BOOL) {
15386       value1 = (value1_str[0] == 'Y' || value1_str[0] == 'y' || value1_str[0] == '1');
15387       value2 = (value2_str[0] == 'Y' || value2_str[0] == 'y' || value2_str[0] == '1');
15388    }
15389 
15390    /* return value */
15391    if (value)
15392       strcpy(value, value1_str);
15393 
15394    /* now do logical operation */
15395    if (strcmp(op, "=") == 0)
15396       return value1 == value2;
15397    if (strcmp(op, "==") == 0)
15398       return value1 == value2;
15399    if (strcmp(op, "!=") == 0)
15400       return value1 != value2;
15401    if (strcmp(op, "<") == 0)
15402       return value1 < value2;
15403    if (strcmp(op, ">") == 0)
15404       return value1 > value2;
15405    if (strcmp(op, "<=") == 0)
15406       return value1 <= value2;
15407    if (strcmp(op, ">=") == 0)
15408       return value1 >= value2;
15409 
15410    return FALSE;
15411 }
15412 
15413 /**dox***************************************************************/
15414 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15415 
15416 /********************************************************************/
15417 /**
15418 Trigger a certain alarm.
15419 \code  ...
15420   lazy.alarm[0] = 0;
15421   size = sizeof(lazy.alarm);
15422   db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
15423 
15424   // trigger alarm if defined
15425   if (lazy.alarm[0])
15426     al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
15427   ...
15428 \endcode
15429 @param alarm_name Alarm name, defined in /alarms/alarms
15430 @param alarm_message Optional message which goes with alarm
15431 @param default_class If alarm is not yet defined under
15432                     /alarms/alarms/<alarm_name>, a new one
15433                     is created and this default class is used.
15434 @param cond_str String displayed in alarm condition
15435 @param type Alarm type, one of AT_xxx
15436 @return AL_SUCCESS, AL_INVALID_NAME
15437 */
15438 INT al_trigger_alarm(char *alarm_name, char *alarm_message, char *default_class, char *cond_str, INT type)
15439 {
15440    if (rpc_is_remote())
15441       return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name, alarm_message, default_class, cond_str, type);
15442 
15443 #ifdef LOCAL_ROUTINES
15444    {
15445       int status, size;
15446       HNDLE hDB, hkeyalarm;
15447       char str[256];
15448       ALARM alarm;
15449       BOOL flag;
15450       ALARM_ODB_STR(alarm_odb_str);
15451 
15452       cm_get_experiment_database(&hDB, NULL);
15453 
15454       /* check online mode */
15455       flag = TRUE;
15456       size = sizeof(flag);
15457       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
15458       if (!flag)
15459          return AL_SUCCESS;
15460 
15461       /* find alarm */
15462       sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15463       db_find_key(hDB, 0, str, &hkeyalarm);
15464       if (!hkeyalarm) {
15465          /* alarm must be an internal analyzer alarm, so create a default alarm */
15466          status = db_create_record(hDB, 0, str, strcomb(alarm_odb_str));
15467          db_find_key(hDB, 0, str, &hkeyalarm);
15468          if (!hkeyalarm) {
15469             cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record");
15470             return AL_ERROR_ODB;
15471          }
15472 
15473          if (default_class && default_class[0])
15474             db_set_value(hDB, hkeyalarm, "Alarm Class", default_class, 32, 1, TID_STRING);
15475          status = TRUE;
15476          db_set_value(hDB, hkeyalarm, "Active", &status, sizeof(status), 1, TID_BOOL);
15477       }
15478 
15479       /* set parameters for internal alarms */
15480       if (type != AT_EVALUATED && type != AT_PERIODIC) {
15481          db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
15482          strcpy(str, cond_str);
15483          db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
15484       }
15485 
15486       size = sizeof(alarm);
15487       status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15488       if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15489          /* make sure alarm record has right structure */
15490          db_check_record(hDB, hkeyalarm, "", strcomb(alarm_odb_str), TRUE);
15491 
15492          size = sizeof(alarm);
15493          status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15494          if (status != DB_SUCCESS) {
15495             cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record");
15496             return AL_ERROR_ODB;
15497          }
15498       }
15499 
15500       /* if internal alarm, check if active and check interval */
15501       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15502          /* check global alarm flag */
15503          flag = TRUE;
15504          size = sizeof(flag);
15505          db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
15506          if (!flag)
15507             return AL_SUCCESS;
15508 
15509          if (!alarm.active)
15510             return AL_SUCCESS;
15511 
15512          if ((INT) ss_time() - (INT) alarm.checked_last < alarm.check_interval)
15513             return AL_SUCCESS;
15514 
15515          /* now the alarm will be triggered, so save time */
15516          alarm.checked_last = ss_time();
15517       }
15518 
15519       /* write back alarm message for internal alarms */
15520       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15521          strncpy(alarm.alarm_message, alarm_message, 79);
15522          alarm.alarm_message[79] = 0;
15523       }
15524 
15525       /* now trigger alarm class defined in this alarm */
15526       if (alarm.alarm_class[0])
15527          al_trigger_class(alarm.alarm_class, alarm_message, alarm.triggered > 0);
15528 
15529       /* signal alarm being triggered */
15530       cm_asctime(str, sizeof(str));
15531 
15532       if (!alarm.triggered)
15533          strcpy(alarm.time_triggered_first, str);
15534 
15535       alarm.triggered++;
15536       strcpy(alarm.time_triggered_last, str);
15537 
15538       alarm.checked_last = ss_time();
15539 
15540       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
15541       if (status != DB_SUCCESS) {
15542          cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record");
15543          return AL_ERROR_ODB;
15544       }
15545 
15546    }
15547 #endif                          /* LOCAL_ROUTINES */
15548 
15549    return AL_SUCCESS;
15550 }
15551 
15552 /**dox***************************************************************/
15553 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15554 
15555 /********************************************************************/
15556 INT al_trigger_class(char *alarm_class, char *alarm_message, BOOL first)
15557 /********************************************************************\
15558 
15559   Routine: al_trigger_class
15560 
15561   Purpose: Trigger a certain alarm class
15562 
15563   Input:
15564     char   *alarm_class     Alarm class, must be defined in
15565                             /alarms/classes
15566     char   *alarm_message   Optional message which goes with alarm
15567     BOOL   first            TRUE if alarm is triggered first time
15568                             (used for elog)
15569 
15570   Output:
15571 
15572   Function value:
15573     AL_INVALID_NAME         Alarm class not defined
15574     AL_SUCCESS              Successful completion
15575 
15576 \********************************************************************/
15577 {
15578    int status, size, state;
15579    HNDLE hDB, hkeyclass;
15580    char str[256], command[256], tag[32], url[256];
15581    ALARM_CLASS ac;
15582 
15583    cm_get_experiment_database(&hDB, NULL);
15584 
15585    /* get alarm class */
15586    sprintf(str, "/Alarms/Classes/%s", alarm_class);
15587    db_find_key(hDB, 0, str, &hkeyclass);
15588    if (!hkeyclass) {
15589       cm_msg(MERROR, "al_trigger_class", "Alarm class %s not found in ODB", alarm_class);
15590       return AL_INVALID_NAME;
15591    }
15592 
15593    size = sizeof(ac);
15594    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
15595    if (status != DB_SUCCESS) {
15596       cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record");
15597       return AL_ERROR_ODB;
15598    }
15599 
15600    /* write system message */
15601    if (ac.write_system_message && (INT) ss_time() - (INT) ac.system_message_last > ac.system_message_interval) {
15602       sprintf(str, "%s: %s", alarm_class, alarm_message);
15603       cm_msg(MTALK, "al_trigger_class", str);
15604       ac.system_message_last = ss_time();
15605    }
15606 
15607    /* write elog message on first trigger if using internal ELOG */
15608    size = sizeof(url);
15609    if (ac.write_elog_message && first &&
15610        db_get_value(hDB, 0, "/Elog/URL", url, &size, TID_STRING, FALSE) != DB_SUCCESS)
15611       el_submit(0, "Alarm system", "Alarm", "General", alarm_class, str,
15612                 "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, 32);
15613 
15614    /* execute command */
15615    if (ac.execute_command[0] &&
15616        ac.execute_interval > 0 && (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
15617       sprintf(str, "%s: %s", alarm_class, alarm_message);
15618       sprintf(command, ac.execute_command, str);
15619       cm_msg(MINFO, "al_trigger_class", "Execute: %s", command);
15620       ss_system(command);
15621       ac.execute_last = ss_time();
15622    }
15623 
15624    /* stop run */
15625    if (ac.stop_run) {
15626       state = STATE_STOPPED;
15627       size = sizeof(state);
15628       db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
15629       if (state != STATE_STOPPED)
15630          cm_transition(TR_STOP, 0, NULL, 0, ASYNC, FALSE);
15631    }
15632 
15633    status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
15634    if (status != DB_SUCCESS) {
15635       cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
15636       return AL_ERROR_ODB;
15637    }
15638 
15639    return AL_SUCCESS;
15640 }
15641 
15642 
15643 /********************************************************************/
15644 INT al_reset_alarm(char *alarm_name)
15645 /********************************************************************\
15646 
15647   Routine: al_reset_alarm
15648 
15649   Purpose: Reset (acknowledge) alarm
15650 
15651   Input:
15652     char   *alarm_name      Alarm name, must be defined in /Alarms/Alarms
15653                             If NULL reset all alarms
15654 
15655   Output:
15656     <none>
15657 
15658   Function value:
15659     AL_INVALID_NAME         Alarm name not defined
15660     AL_RESET                Alarm was triggered and reset
15661     AL_SUCCESS              Successful completion
15662 
15663 \********************************************************************/
15664 {
15665    int status, size, i;
15666    HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
15667    KEY key;
15668    char str[256];
15669    ALARM alarm;
15670    ALARM_CLASS ac;
15671 
15672    cm_get_experiment_database(&hDB, NULL);
15673 
15674    if (alarm_name == NULL) {
15675       /* reset all alarms */
15676       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
15677       if (hkeyalarm) {
15678          for (i = 0;; i++) {
15679             db_enum_link(hDB, hkeyalarm, i, &hsubkey);
15680 
15681             if (!hsubkey)
15682                break;
15683 
15684             db_get_key(hDB, hsubkey, &key);
15685             al_reset_alarm(key.name);
15686          }
15687       }
15688       return AL_SUCCESS;
15689    }
15690 
15691    /* find alarm and alarm class */
15692    sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15693    db_find_key(hDB, 0, str, &hkeyalarm);
15694    if (!hkeyalarm) {
15695       cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB", alarm_name);
15696       return AL_INVALID_NAME;
15697    }
15698 
15699    size = sizeof(alarm);
15700    status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15701    if (status != DB_SUCCESS) {
15702       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
15703       return AL_ERROR_ODB;
15704    }
15705 
15706    sprintf(str, "/Alarms/Classes/%s", alarm.alarm_class);
15707    db_find_key(hDB, 0, str, &hkeyclass);
15708    if (!hkeyclass) {
15709       cm_msg(MERROR, "al_reset_alarm", "Alarm class %s not found in ODB", alarm.alarm_class);
15710       return AL_INVALID_NAME;
15711    }
15712 
15713    size = sizeof(ac);
15714    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
15715    if (status != DB_SUCCESS) {
15716       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
15717       return AL_ERROR_ODB;
15718    }
15719 
15720    if (alarm.triggered) {
15721       alarm.triggered = 0;
15722       alarm.time_triggered_first[0] = 0;
15723       alarm.time_triggered_last[0] = 0;
15724       alarm.checked_last = 0;
15725 
15726       ac.system_message_last = 0;
15727       ac.execute_last = 0;
15728 
15729       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
15730       if (status != DB_SUCCESS) {
15731          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
15732          return AL_ERROR_ODB;
15733       }
15734       status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
15735       if (status != DB_SUCCESS) {
15736          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
15737          return AL_ERROR_ODB;
15738       }
15739       return AL_RESET;
15740    }
15741 
15742    return AL_SUCCESS;
15743 }
15744 
15745 
15746 /********************************************************************/
15747 INT al_check()
15748 /********************************************************************\
15749 
15750   Routine: al_scan
15751 
15752   Purpose: Scan ODB alarams and programs
15753 
15754   Input:
15755 
15756   Output:
15757 
15758   Function value:
15759     AL_SUCCESS              Successful completion
15760 
15761 \********************************************************************/
15762 {
15763    if (rpc_is_remote())
15764       return rpc_call(RPC_AL_CHECK);
15765 
15766 #ifdef LOCAL_ROUTINES
15767    {
15768       INT i, status, size, mutex;
15769       HNDLE hDB, hkeyroot, hkey;
15770       KEY key;
15771       ALARM alarm;
15772       char str[256], value[256];
15773       time_t now;
15774       PROGRAM_INFO program_info;
15775       BOOL flag;
15776 
15777       ALARM_CLASS_STR(alarm_class_str);
15778       ALARM_ODB_STR(alarm_odb_str);
15779       ALARM_PERIODIC_STR(alarm_periodic_str);
15780 
15781       cm_get_experiment_database(&hDB, NULL);
15782 
15783       if (hDB == 0)
15784          return AL_SUCCESS;     /* called from server not yet connected */
15785 
15786       /* check online mode */
15787       flag = TRUE;
15788       size = sizeof(flag);
15789       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
15790       if (!flag)
15791          return AL_SUCCESS;
15792 
15793       /* check global alarm flag */
15794       flag = TRUE;
15795       size = sizeof(flag);
15796       db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
15797       if (!flag)
15798          return AL_SUCCESS;
15799 
15800       /* request semaphore */
15801       cm_get_experiment_mutex(&mutex, NULL);
15802       status = ss_mutex_wait_for(mutex, 100);
15803       if (status != SS_SUCCESS)
15804          return SUCCESS;        /* someone else is doing alarm business */
15805 
15806       /* check ODB alarms */
15807       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15808       if (!hkeyroot) {
15809          /* create default ODB alarm */
15810          status = db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB", strcomb(alarm_odb_str));
15811          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15812          if (!hkeyroot) {
15813             ss_mutex_release(mutex);
15814             return SUCCESS;
15815          }
15816 
15817          status = db_create_record(hDB, 0, "/Alarms/Alarms/Demo periodic", strcomb(alarm_periodic_str));
15818          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
15819          if (!hkeyroot) {
15820             ss_mutex_release(mutex);
15821             return SUCCESS;
15822          }
15823 
15824          /* create default alarm classes */
15825          status = db_create_record(hDB, 0, "/Alarms/Classes/Alarm", strcomb(alarm_class_str));
15826          status = db_create_record(hDB, 0, "/Alarms/Classes/Warning", strcomb(alarm_class_str));
15827          if (status != DB_SUCCESS) {
15828             ss_mutex_release(mutex);
15829             return SUCCESS;
15830          }
15831       }
15832 
15833       for (i = 0;; i++) {
15834          status = db_enum_key(hDB, hkeyroot, i, &hkey);
15835          if (status == DB_NO_MORE_SUBKEYS)
15836             break;
15837 
15838          db_get_key(hDB, hkey, &key);
15839 
15840          size = sizeof(alarm);
15841          status = db_get_record(hDB, hkey, &alarm, &size, 0);
15842          if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15843             /* make sure alarm record has right structure */
15844             db_check_record(hDB, hkey, "", strcomb(alarm_odb_str), TRUE);
15845             size = sizeof(alarm);
15846             status = db_get_record(hDB, hkey, &alarm, &size, 0);
15847             if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15848                cm_msg(MERROR, "al_check", "Cannot get alarm record");
15849                continue;
15850             }
15851          }
15852 
15853          /* check periodic alarm only when active */
15854          if (alarm.active &&
15855              alarm.type == AT_PERIODIC &&
15856              alarm.check_interval > 0 && (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
15857             /* if checked_last has not been set, set it to current time */
15858             if (alarm.checked_last == 0) {
15859                alarm.checked_last = ss_time();
15860                db_set_record(hDB, hkey, &alarm, size, 0);
15861             } else
15862                al_trigger_alarm(key.name, alarm.alarm_message, alarm.alarm_class, "", AT_PERIODIC);
15863          }
15864 
15865          /* check alarm only when active and not internal */
15866          if (alarm.active &&
15867              alarm.type == AT_EVALUATED &&
15868              alarm.check_interval > 0 && (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
15869             /* if condition is true, trigger alarm */
15870             if (al_evaluate_condition(alarm.condition, value)) {
15871                sprintf(str, alarm.alarm_message, value);
15872                al_trigger_alarm(key.name, str, alarm.alarm_class, "", AT_EVALUATED);
15873             } else {
15874                alarm.checked_last = ss_time();
15875                status = db_set_record(hDB, hkey, &alarm, sizeof(alarm), 0);
15876                if (status != DB_SUCCESS) {
15877                   cm_msg(MERROR, "al_check", "Cannot write back alarm record");
15878                   continue;
15879                }
15880             }
15881          }
15882       }
15883 
15884       /* check /programs alarms */
15885       db_find_key(hDB, 0, "/Programs", &hkeyroot);
15886       if (hkeyroot) {
15887          for (i = 0;; i++) {
15888             status = db_enum_key(hDB, hkeyroot, i, &hkey);
15889             if (status == DB_NO_MORE_SUBKEYS)
15890                break;
15891 
15892             db_get_key(hDB, hkey, &key);
15893 
15894             /* don't check "execute on xxx" */
15895             if (key.type != TID_KEY)
15896                continue;
15897 
15898             size = sizeof(program_info);
15899             status = db_get_record(hDB, hkey, &program_info, &size, 0);
15900             if (status != DB_SUCCESS) {
15901                cm_msg(MERROR, "al_check", "Cannot get program info record");
15902                continue;
15903             }
15904 
15905             now = ss_time();
15906 
15907             rpc_get_name(str);
15908             str[strlen(key.name)] = 0;
15909             if (!equal_ustring(str, key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
15910                if (program_info.first_failed == 0)
15911                   program_info.first_failed = now;
15912 
15913                /* fire alarm when not running for more than what specified in check interval */
15914                if (now - program_info.first_failed >= program_info.check_interval / 1000) {
15915                   /* if not running and alarm calss defined, trigger alarm */
15916                   if (program_info.alarm_class[0]) {
15917                      sprintf(str, "Program %s is not running", key.name);
15918                      al_trigger_alarm(key.name, str, program_info.alarm_class,
15919                                       "Program not running", AT_PROGRAM);
15920                   }
15921 
15922                   /* auto restart program */
15923                   if (program_info.auto_restart && program_info.start_command[0]) {
15924                      ss_system(program_info.start_command);
15925                      program_info.first_failed = 0;
15926                      cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
15927                   }
15928                }
15929             } else
15930                program_info.first_failed = 0;
15931 
15932             db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
15933          }
15934       }
15935 
15936       ss_mutex_release(mutex);
15937    }
15938 #endif                          /* LOCAL_COUTINES */
15939 
15940    return SUCCESS;
15941 }
15942 
15943 /**dox***************************************************************/
15944 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15945 
15946                                                                                                              /** @} *//* end of alfunctionc */
15947 
15948 /***** sKIP eb_xxx **************************************************/
15949 /**dox***************************************************************/
15950 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15951 /***** sKIP eb_xxx **************************************************/
15952 
15953 #if !defined(OS_VXWORKS)
15954 /********************************************************************\
15955 *                                                                    *
15956 *                 Event buffer functions                             *
15957 *                                                                    *
15958 \********************************************************************/
15959 
15960 /* PAA several modification in the eb_xxx()
15961    also new function eb_buffer_full()
15962 */
15963 static char *_event_ring_buffer = NULL;
15964 static INT _eb_size;
15965 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
15966 
15967 /********************************************************************/
15968 INT eb_create_buffer(INT size)
15969 /********************************************************************\
15970 
15971   Routine: eb_create_buffer
15972 
15973   Purpose: Create an event buffer. Has to be called initially before
15974            any other eb_xxx function
15975 
15976   Input:
15977     INT    size             Size in bytes
15978 
15979   Output:
15980     none
15981 
15982   Function value:
15983     CM_SUCCESS              Successful completion
15984     BM_NO_MEMEORY           Out of memory
15985 
15986 \********************************************************************/
15987 {
15988    _event_ring_buffer = (char *) M_MALLOC(size);
15989    if (_event_ring_buffer == NULL)
15990       return BM_NO_MEMORY;
15991 
15992    memset(_event_ring_buffer, 0, size);
15993    _eb_size = size;
15994 
15995    _eb_write_pointer = _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
15996 
15997    _send_sock = rpc_get_event_sock();
15998 
15999    return CM_SUCCESS;
16000 }
16001 
16002 /********************************************************************/
16003 INT eb_free_buffer()
16004 /********************************************************************\
16005 
16006   Routine: eb_free_buffer
16007 
16008   Purpose: Free memory allocated voa eb_create_buffer
16009 
16010   Input:
16011     none
16012 
16013   Output:
16014     none
16015 
16016   Function value:
16017     CM_SUCCESS              Successful completion
16018 
16019 \********************************************************************/
16020 {
16021    if (_event_ring_buffer)
16022       M_FREE(_event_ring_buffer);
16023 
16024    _eb_size = 0;
16025    return CM_SUCCESS;
16026 }
16027 
16028 
16029 /********************************************************************/
16030 INT eb_free_space(void)
16031 /********************************************************************\
16032 
16033   Routine: eb_free_space
16034 
16035   Purpose: Compute and return usable free space in the event buffer
16036 
16037   Input:
16038     none
16039 
16040   Output:
16041     none
16042 
16043   Function value:
16044     INT    Number of usable free bytes in the event buffer
16045 
16046 \********************************************************************/
16047 {
16048    INT free;
16049 
16050    if (_event_ring_buffer == NULL) {
16051       cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
16052       return -1;
16053    }
16054 
16055    if (_eb_write_pointer >= _eb_read_pointer) {
16056       free = _eb_size - ((POINTER_T) _eb_write_pointer - (POINTER_T) _event_ring_buffer);
16057    } else if (_eb_write_pointer >= _event_ring_buffer) {
16058       free = (POINTER_T) _eb_read_pointer - (POINTER_T) _eb_write_pointer;
16059    } else if (_eb_end_pointer == _event_ring_buffer) {
16060       _eb_write_pointer = _event_ring_buffer;
16061       free = _eb_size;
16062    } else if (_eb_read_pointer == _event_ring_buffer) {
16063       free = 0;
16064    } else {
16065       _eb_write_pointer = _event_ring_buffer;
16066       free = (POINTER_T) _eb_read_pointer - (POINTER_T) _eb_write_pointer;
16067    }
16068 
16069    return free;
16070 }
16071 
16072 
16073 /********************************************************************/
16074 DWORD eb_get_level()
16075 /********************************************************************\
16076 
16077   Routine: eb_get_level
16078 
16079   Purpose: Return filling level of event buffer in percent
16080 
16081   Input:
16082     none
16083 
16084   Output:
16085     none
16086 
16087   Function value:
16088     DWORD level              0..99
16089 
16090 \********************************************************************/
16091 {
16092    INT size;
16093 
16094    size = _eb_size - eb_free_space();
16095 
16096    return (100 * size) / _eb_size;
16097 }
16098 
16099 
16100 /********************************************************************/
16101 BOOL eb_buffer_full(void)
16102 /********************************************************************\
16103 
16104   Routine: eb_buffer_full
16105 
16106   Purpose: Test if there is sufficient space in the event buffer
16107     for another event
16108 
16109   Input:
16110     none
16111 
16112   Output:
16113     none
16114 
16115   Function value:
16116     BOOL  Is there enough space for another event in the event buffer
16117 
16118 \********************************************************************/
16119 {
16120    INT free;
16121 
16122    free = eb_free_space();
16123 
16124    /* if max. event won't fit, return zero */
16125    return (free < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
16126 }
16127 
16128 
16129 /********************************************************************/
16130 EVENT_HEADER *eb_get_pointer()
16131 /********************************************************************\
16132 
16133   Routine: eb_get_pointer
16134 
16135   Purpose: Get pointer to next free location in event buffer
16136 
16137   Input:
16138     none
16139 
16140   Output:
16141     none
16142 
16143   Function value:
16144     EVENT_HEADER *            Pointer to free location
16145 
16146 \********************************************************************/
16147 {
16148    /* if max. event won't fit, return zero */
16149    if (eb_buffer_full()) {
16150 #ifdef OS_VXWORKS
16151       logMsg("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
16152              _eb_read_pointer - _event_ring_buffer,
16153              _eb_write_pointer - _event_ring_buffer, _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
16154 #endif
16155       return NULL;
16156    }
16157 
16158    /* leave space for buffer handle */
16159    return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
16160 }
16161 
16162 
16163 /********************************************************************/
16164 INT eb_increment_pointer(INT buffer_handle, INT event_size)
16165 /********************************************************************\
16166 
16167   Routine: eb_increment_pointer
16168 
16169   Purpose: Increment write pointer of event buffer after an event
16170            has been copied into the buffer (at an address previously
16171            obtained via eb_get_pointer)
16172 
16173   Input:
16174     INT buffer_handle         Buffer handle event should be sent to
16175     INT event_size            Event size in bytes including header
16176 
16177   Output:
16178     none
16179 
16180   Function value:
16181     CM_SUCCESS                Successful completion
16182 
16183 \********************************************************************/
16184 {
16185    INT aligned_event_size;
16186 
16187    /* if not connected remotely, use bm_send_event */
16188    if (_send_sock == 0)
16189       return bm_send_event(buffer_handle, _eb_write_pointer + sizeof(INT), event_size, SYNC);
16190 
16191    aligned_event_size = ALIGN8(event_size);
16192 
16193    /* copy buffer handle */
16194    *((INT *) _eb_write_pointer) = buffer_handle;
16195    _eb_write_pointer += sizeof(INT) + aligned_event_size;
16196 
16197    if (_eb_write_pointer > _eb_end_pointer)
16198       _eb_end_pointer = _eb_write_pointer;
16199 
16200    if (_eb_write_pointer > _event_ring_buffer + _eb_size)
16201       cm_msg(MERROR, "eb_increment_pointer",
16202              "event size (%d) exeeds maximum event size (%d)", event_size, MAX_EVENT_SIZE);
16203 
16204    if (_eb_size - ((POINTER_T) _eb_write_pointer - (POINTER_T) _event_ring_buffer) <
16205        MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT)) {
16206       _eb_write_pointer = _event_ring_buffer;
16207 
16208       /* avoid rp==wp */
16209       if (_eb_read_pointer == _event_ring_buffer)
16210          _eb_write_pointer--;
16211    }
16212 
16213    return CM_SUCCESS;
16214 }
16215 
16216 
16217 /********************************************************************/
16218 INT eb_send_events(BOOL send_all)
16219 /********************************************************************\
16220 
16221   Routine: eb_send_events
16222 
16223   Purpose: Send events from the event buffer to the server
16224 
16225   Input:
16226     BOOL send_all             If FALSE, only send events if buffer
16227                               contains more than _opt_tcp_size bytes
16228 
16229   Output:
16230     none
16231 
16232   Function value:
16233     CM_SUCCESS                Successful completion
16234 
16235 \********************************************************************/
16236 {
16237    char *eb_wp, *eb_ep;
16238    INT size, i;
16239 
16240    /* write pointers are volatile, so make copy */
16241    eb_ep = _eb_end_pointer;
16242    eb_wp = _eb_write_pointer;
16243 
16244    if (eb_wp == _eb_read_pointer)
16245       return CM_SUCCESS;
16246    if (eb_wp > _eb_read_pointer) {
16247       size = (POINTER_T) eb_wp - (POINTER_T) _eb_read_pointer;
16248 
16249       /* don't send if less than optimal TCP buffer size available */
16250       if (size < (INT) _opt_tcp_size && !send_all)
16251          return CM_SUCCESS;
16252    } else {
16253       /* send last piece of event buffer */
16254       size = (POINTER_T) eb_ep - (POINTER_T) _eb_read_pointer;
16255    }
16256 
16257    while (size > _opt_tcp_size) {
16258       /* send buffer */
16259       i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
16260       if (i < 0) {
16261          printf("send_tcp() returned %d\n", i);
16262          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16263          return RPC_NET_ERROR;
16264       }
16265 
16266       _eb_read_pointer += _opt_tcp_size;
16267       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16268          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16269 
16270       size -= _opt_tcp_size;
16271    }
16272 
16273    if (send_all || eb_wp < _eb_read_pointer) {
16274       /* send buffer */
16275       i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
16276       if (i < 0) {
16277          printf("send_tcp() returned %d\n", i);
16278          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16279          return RPC_NET_ERROR;
16280       }
16281 
16282       _eb_read_pointer += size;
16283       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16284          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16285    }
16286 
16287    /* Check for case where eb_wp = eb_ring_buffer - 1 */
16288    if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
16289       return CM_SUCCESS;
16290    }
16291 
16292    if (eb_wp != _eb_read_pointer)
16293       return BM_MORE_EVENTS;
16294 
16295    return CM_SUCCESS;
16296 }
16297 
16298 #endif                          /* OS_VXWORKS  eb section */
16299 
16300 /**dox***************************************************************/
16301 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16302 
16303 /**dox***************************************************************/
16304 /** @addtogroup dmfunctionc
16305  *
16306  *  @{  */
16307 
16308 /**dox***************************************************************/
16309 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16310 
16311 /********************************************************************\
16312 *                                                                    *
16313 *                 Dual memory buffer functions                       *
16314 *                                                                    *
16315 * Provide a dual memory buffer scheme for handling front-end         *
16316 * event. This code as been requested for allowing contemporary       *
16317 * task handling a)acquisition, b)network transfer if possible.       *
16318 * The pre-compiler switch will determine the mode of operation.      *
16319 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
16320 * a seperate task taking care of the dm_area_send                    *
16321 *                                                                    *
16322 * "*" : visible functions                                            *
16323 * dm_buffer_create():     *Setup the dual memory buffer              *
16324 *                          Setup semaphore                           *
16325 *                          Spawn second thread                       *
16326 * dm_buffer_release():    *Release memory allocation for dm          *
16327 *                          Force a kill of 2nd thread                *
16328 *                          Remove semaphore                          *
16329 * dm_area_full():         *Check for both area being full            *
16330 *                          None blocking, may be used for interrupt  *
16331 *                          disable.                                  *
16332 * dm_pointer_get()     :  *Check memory space and return pointer     *
16333 *                          Blocking function with timeout if no more *
16334 *                          space for next event. If error will abort.*
16335 * dm_pointer_increment(): *Move pointer to next free location        *
16336 *                          None blocking. performs bm_send_event if  *
16337 *                          local connection.                         *
16338 * dm_area_send():         *Transfer FULL buffer(s)                   *
16339 *                          None blocking function.                   *
16340 *                          if DUAL_THREAD: Give sem_send semaphore   *
16341 *                          else transfer FULL buffer                 *
16342 * dm_area_flush():        *Transfer all remaining events from dm     *
16343 *                          Blocking function with timeout            *
16344 *                          if DUAL_THREAD: Give sem_flush semaphore. *
16345 * dm_task():               Secondary thread handling DUAL_THREAD     *
16346 *                          mechanism. Serves 2 requests:             *
16347 *                          dm_send:  Transfer FULL buffer only.      *
16348 *                          dm_flush: Transfer ALL buffers.           *
16349 * dm_area_switch():        internal, used by dm_pointer_get()        *
16350 * dm_active_full():        internal: check space in current buffer   *
16351 * dm_buffer_send():        internal: send data for given area        *
16352 * dm_buffer_time_get():    interal: return the time stamp of the     *
16353 *                          last switch                               *
16354 \********************************************************************/
16355 
16356 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
16357 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
16358 #define DM_KILL        12       /* Kill request for 2nd thread */
16359 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
16360 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
16361 
16362 typedef struct {
16363    char *pt;                    /* top pointer    memory buffer          */
16364    char *pw;                    /* write pointer  memory buffer          */
16365    char *pe;                    /* end   pointer  memory buffer          */
16366    char *pb;                    /* bottom pointer memory buffer          */
16367    BOOL full;                   /* TRUE if memory buffer is full         */
16368    DWORD serial;                /* full buffer serial# for evt order     */
16369 } DMEM_AREA;
16370 
16371 typedef struct {
16372    DMEM_AREA *pa;               /* active memory buffer */
16373    DMEM_AREA area1;             /* mem buffer area 1 */
16374    DMEM_AREA area2;             /* mem buffer area 2 */
16375    DWORD serial;                /* overall buffer serial# for evt order     */
16376    INT action;                  /* for multi thread configuration */
16377    DWORD last_active;           /* switch time stamp */
16378    HNDLE sem_send;              /* semaphore for dm_task */
16379    HNDLE sem_flush;             /* semaphore for dm_task */
16380 } DMEM_BUFFER;
16381 
16382 DMEM_BUFFER dm;
16383 INT dm_user_max_event_size;
16384 
16385 /**dox***************************************************************/
16386 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16387 
16388 /********************************************************************/
16389 /**
16390 Setup a dual memory buffer. Has to be called initially before
16391            any other dm_xxx function
16392 @param size             Size in bytes
16393 @param user_max_event_size max event size
16394 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
16395 */
16396 INT dm_buffer_create(INT size, INT user_max_event_size)
16397 {
16398 
16399    dm.area1.pt = (char *) M_MALLOC(size);
16400    if (dm.area1.pt == NULL)
16401       return (BM_NO_MEMORY);
16402    dm.area2.pt = (char *) M_MALLOC(size);
16403    if (dm.area2.pt == NULL)
16404       return (BM_NO_MEMORY);
16405 
16406    /* check user event size against the system MAX_EVENT_SIZE */
16407    if (user_max_event_size > MAX_EVENT_SIZE) {
16408       cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
16409       return BM_MEMSIZE_MISMATCH;
16410    }
16411    dm_user_max_event_size = user_max_event_size;
16412 
16413    memset(dm.area1.pt, 0, size);
16414    memset(dm.area2.pt, 0, size);
16415 
16416    /* initialize pointers */
16417    dm.area1.pb = dm.area1.pt + size - 1024;
16418    dm.area1.pw = dm.area1.pe = dm.area1.pt;
16419    dm.area2.pb = dm.area2.pt + size - 1024;
16420    dm.area2.pw = dm.area2.pe = dm.area2.pt;
16421 
16422   /*-PAA-*/
16423 #ifdef DM_DEBUG
16424    printf(" in dm_buffer_create ---------------------------------\n");
16425    printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw, dm.area1.pe, dm.area1.pb);
16426    printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw, dm.area2.pe, dm.area2.pb);
16427 #endif
16428 
16429    /* activate first area */
16430    dm.pa = &dm.area1;
16431 
16432    /* Default not full */
16433    dm.area1.full = dm.area2.full = FALSE;
16434 
16435    /* Reset serial buffer number with proper starting sequence */
16436    dm.area1.serial = dm.area2.serial = 0;
16437    /* ensure proper serial on next increment */
16438    dm.serial = 1;
16439 
16440    /* set active buffer time stamp */
16441    dm.last_active = ss_millitime();
16442 
16443    /* get socket for event sending */
16444    _send_sock = rpc_get_event_sock();
16445 
16446 #ifdef DM_DUAL_THREAD
16447    {
16448       INT status;
16449       VX_TASK_SPAWN starg;
16450 
16451       /* create semaphore */
16452       status = ss_mutex_create("send", &dm.sem_send);
16453       if (status != SS_CREATED && status != SS_SUCCESS) {
16454          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
16455          return status;
16456       }
16457       status = ss_mutex_create("flush", &dm.sem_flush);
16458       if (status != SS_CREATED && status != SS_SUCCESS) {
16459          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
16460          return status;
16461       }
16462       /* spawn dm_task */
16463       memset(&starg, 0, sizeof(VX_TASK_SPAWN));
16464 
16465 #ifdef OS_VXWORKS
16466       /* Fill up the necessary arguments */
16467       strcpy(starg.name, "areaSend");
16468       starg.priority = 120;
16469       starg.stackSize = 20000;
16470 #endif
16471 
16472       if ((status = ss_thread_create(dm_task, (void *) &starg))
16473           != SS_SUCCESS) {
16474          cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
16475          return status;
16476       }
16477 #ifdef OS_WINNT
16478       /* necessary for true MUTEX (NT) */
16479       ss_mutex_wait_for(dm.sem_send, 0);
16480 #endif
16481    }
16482 #endif                          /* DM_DUAL_THREAD */
16483 
16484    return CM_SUCCESS;
16485 }
16486 
16487 /**dox***************************************************************/
16488 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16489 
16490 /********************************************************************/
16491 INT dm_buffer_release(void)
16492 /********************************************************************\
16493   Routine: dm_buffer_release
16494 
16495   Purpose: Release dual memory buffers
16496   Input:
16497     none
16498   Output:
16499     none
16500   Function value:
16501     CM_SUCCESS              Successful completion
16502 \********************************************************************/
16503 {
16504    if (dm.area1.pt) {
16505       free(dm.area1.pt);
16506       dm.area1.pt = NULL;
16507    }
16508    if (dm.area2.pt) {
16509       free(dm.area2.pt);
16510       dm.area2.pt = NULL;
16511    }
16512    dm.serial = 0;
16513    dm.area1.full = dm.area2.full = TRUE;
16514    dm.area1.serial = dm.area2.serial = 0;
16515 
16516 #ifdef DM_DUAL_THREAD
16517    /* kill spawned dm_task */
16518    dm.action = DM_KILL;
16519    ss_mutex_release(dm.sem_send);
16520    ss_mutex_release(dm.sem_flush);
16521 
16522    /* release semaphore */
16523    ss_mutex_delete(dm.sem_send, 0);
16524    ss_mutex_delete(dm.sem_flush, 0);
16525 #endif
16526 
16527    return CM_SUCCESS;
16528 }
16529 
16530 /********************************************************************/
16531 INLINE DMEM_AREA *dm_area_switch(void)
16532 /********************************************************************\
16533   Routine: dm_area_switch
16534 
16535   Purpose: set active area to the other empty area or NULL if both
16536            area are full. May have to check the serial consistancy...
16537   Input:
16538     none
16539   Output:
16540     none
16541   Function value:
16542     DMEM_AREA *            Pointer to active area or both full
16543 \********************************************************************/
16544 {
16545    volatile BOOL full1, full2;
16546 
16547    full1 = dm.area1.full;
16548    full2 = dm.area2.full;
16549 
16550    if (!full1 && !full2) {
16551       if (dm.area1.serial <= dm.area2.serial)
16552          return (&(dm.area1));
16553       else
16554          return (&(dm.area2));
16555    }
16556 
16557    if (!full1) {
16558       return (&(dm.area1));
16559    } else if (!full2) {
16560       return (&(dm.area2));
16561    }
16562    return (NULL);
16563 }
16564 
16565 /********************************************************************/
16566 INLINE BOOL dm_area_full(void)
16567 /********************************************************************\
16568   Routine: dm_area_full
16569 
16570   Purpose: Test if both area are full in order to block interrupt
16571   Input:
16572     none
16573   Output:
16574     none
16575   Function value:
16576     BOOL         TRUE if not enough space for another event
16577 \********************************************************************/
16578 {
16579    if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
16580       return TRUE;
16581    return FALSE;
16582 }
16583 
16584 /********************************************************************/
16585 INLINE BOOL dm_active_full(void)
16586 /********************************************************************\
16587   Routine: dm_active_full
16588 
16589   Purpose: Test if there is sufficient space in either event buffer
16590            for another event.
16591   Input:
16592     none
16593   Output:
16594     none
16595   Function value:
16596     BOOL         TRUE if not enough space for another event
16597 \********************************************************************/
16598 {
16599    /* catch both full areas, waiting for transfer */
16600    if (dm.pa == NULL)
16601       return TRUE;
16602    /* Check the space in the active buffer only
16603       as I don't switch buffer here */
16604    if (dm.pa->full)
16605       return TRUE;
16606    return (((POINTER_T) dm.pa->pb - (POINTER_T) dm.pa->pw) < (INT)
16607            (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
16608 }
16609 
16610 /********************************************************************/
16611 DWORD dm_buffer_time_get(void)
16612 /********************************************************************\
16613   Routine: dm_buffer_time_get
16614 
16615   Purpose: return the time from the last buffer switch.
16616 
16617   Input:
16618     none
16619   Output:
16620     none
16621   Function value:
16622     DWORD        time stamp
16623 
16624 \********************************************************************/
16625 {
16626    return (dm.last_active);
16627 }
16628 
16629 
16630 /********************************************************************/
16631 EVENT_HEADER *dm_pointer_get(void)
16632 /********************************************************************\
16633   Routine: dm_pointer_get
16634 
16635   Purpose: Get pointer to next free location in event buffer.
16636            after 10sec tries, it times out return NULL indicating a
16637            serious problem, i.e. abort.
16638   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
16639   Input:
16640     none
16641   Output:
16642     DM_BUFFER * dm    local valid dm to work on
16643   Function value:
16644     EVENT_HEADER *    Pointer to free location
16645     NULL              cannot after several attempt get free space => abort
16646 \********************************************************************/
16647 {
16648    int timeout, status;
16649 
16650    /* Is there still space in the active area ? */
16651    if (!dm_active_full())
16652       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
16653 
16654    /* no more space => switch area */
16655 
16656    /* Tag current area with global dm.serial for order consistency */
16657    dm.pa->serial = dm.serial++;
16658 
16659    /* set active buffer time stamp */
16660    dm.last_active = ss_millitime();
16661 
16662    /* mark current area full */
16663    dm.pa->full = TRUE;
16664 
16665    /* Trigger/do data transfer (Now/don't wait) */
16666    if ((status = dm_area_send()) == RPC_NET_ERROR) {
16667       cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
16668       return NULL;
16669    }
16670 
16671    /* wait switch completion (max 10 sec) */
16672    timeout = ss_millitime();    /* with timeout */
16673    while ((ss_millitime() - timeout) < 10000) {
16674       dm.pa = dm_area_switch();
16675       if (dm.pa != NULL)
16676          return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
16677       ss_sleep(200);
16678 #ifdef DM_DEBUG
16679       printf(" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
16680              ss_millitime() - timeout, dm.area1.full, dm.area2.full, dm.area1.serial,
16681              dm.area2.serial, dm.serial);
16682 #endif
16683    }
16684 
16685    /* Time running out abort */
16686    cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
16687    return NULL;
16688 }
16689 
16690 
16691 /********************************************************************/
16692 int dm_pointer_increment(INT buffer_handle, INT event_size)
16693 /********************************************************************\
16694   Routine: dm_pointer_increment
16695 
16696   Purpose: Increment write pointer of event buffer after an event
16697            has been copied into the buffer (at an address previously
16698            obtained via dm_pointer_get)
16699   Input:
16700     INT buffer_handle         Buffer handle event should be sent to
16701     INT event_size            Event size in bytes including header
16702   Output:
16703     none
16704   Function value:
16705     CM_SUCCESS                Successful completion
16706     status                    from bm_send_event for local connection
16707 \********************************************************************/
16708 {
16709    INT aligned_event_size;
16710 
16711    /* if not connected remotely, use bm_send_event */
16712    if (_send_sock == 0) {
16713       *((INT *) dm.pa->pw) = buffer_handle;
16714       return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT), event_size, SYNC);
16715    }
16716    aligned_event_size = ALIGN8(event_size);
16717 
16718    *((INT *) dm.pa->pw) = buffer_handle;
16719 
16720    /* adjust write pointer */
16721    dm.pa->pw += sizeof(INT) + aligned_event_size;
16722 
16723    /* adjust end pointer */
16724    dm.pa->pe = dm.pa->pw;
16725 
16726    return CM_SUCCESS;
16727 }
16728 
16729 /********************************************************************/
16730 INLINE INT dm_buffer_send(DMEM_AREA * larea)
16731 /********************************************************************\
16732   Routine: dm_buffer_send
16733 
16734   Purpose: Ship data to the cache in fact!
16735            Basically the same do loop is done in the send_tcp.
16736            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
16737            Introduced for bringing tcp option to user code.
16738   Input:
16739     DMEM_AREA * larea   The area to work with.
16740   Output:
16741     none
16742   Function value:
16743     CM_SUCCESS       Successful completion
16744     DM_ACTIVE_NULL   Both area were/are full
16745     RPC_NET_ERROR    send error
16746 \********************************************************************/
16747 {
16748    INT tot_size, nwrite;
16749    char *lpt;
16750 
16751    /* if not connected remotely, use bm_send_event */
16752    if (_send_sock == 0)
16753       return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
16754 
16755    /* alias */
16756    lpt = larea->pt;
16757 
16758    /* Get overall buffer size */
16759    tot_size = (POINTER_T) larea->pe - (POINTER_T) lpt;
16760 
16761    /* shortcut for speed */
16762    if (tot_size == 0)
16763       return CM_SUCCESS;
16764 
16765 #ifdef DM_DEBUG
16766    printf("lpt:%p size:%i ", lpt, tot_size);
16767 #endif
16768    nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
16769 #ifdef DM_DEBUG
16770    printf("nwrite:%i  errno:%i\n", nwrite, errno);
16771 #endif
16772    if (nwrite < 0)
16773       return RPC_NET_ERROR;
16774 
16775    /* reset area */
16776    larea->pw = larea->pe = larea->pt;
16777    larea->full = FALSE;
16778    return CM_SUCCESS;
16779 }
16780 
16781 /********************************************************************/
16782 INT dm_area_send(void)
16783 /********************************************************************\
16784   Routine: dm_area_send
16785 
16786   Purpose: Empty the FULL area only in proper event order
16787            Meant to be use either in mfe.c scheduler on every event
16788 
16789   Dual memory scheme:
16790    DM_DUAL_THREAD : Trigger sem_send
16791    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
16792                     if dm.pa is NULL (were both full) and now both are empty
16793 
16794   Input:
16795     none
16796   Output:
16797     none
16798   Function value:
16799     CM_SUCCESS                Successful completion
16800     RPC_NET_ERROR             send error
16801 \********************************************************************/
16802 {
16803 #ifdef DM_DUAL_THREAD
16804    INT status;
16805 
16806    /* force a DM_SEND if possible. Don't wait for completion */
16807    dm.action = DM_SEND;
16808    ss_mutex_release(dm.sem_send);
16809 #ifdef OS_WINNT
16810    /* necessary for true MUTEX (NT) */
16811    status = ss_mutex_wait_for(dm.sem_send, 1);
16812    if (status == SS_NO_MUTEX) {
16813       printf(" timeout while waiting for sem_send\n");
16814       return RPC_NET_ERROR;
16815    }
16816 #endif
16817 
16818    return CM_SUCCESS;
16819 #else
16820    /* ---------- NOT IN DUAL THREAD ----------- */
16821    INT status = 0;
16822 
16823    /* if no DUAL thread everything is local then */
16824    /* select the full area */
16825    if (dm.area1.full && dm.area2.full)
16826       if (dm.area1.serial <= dm.area2.serial)
16827          status = dm_buffer_send(&dm.area1);
16828       else
16829          status = dm_buffer_send(&dm.area2);
16830    else if (dm.area1.full)
16831       status = dm_buffer_send(&dm.area1);
16832    else if (dm.area2.full)
16833       status = dm_buffer_send(&dm.area2);
16834    if (status != CM_SUCCESS)
16835       return status;            /* catch transfer error too */
16836 
16837    if (dm.pa == NULL) {
16838       printf(" sync send dm.pa:%p full 1%d 2%d\n", dm.pa, dm.area1.full, dm.area2.full);
16839       dm.pa = &dm.area1;
16840    }
16841    return CM_SUCCESS;
16842 #endif
16843 }
16844 
16845 /********************************************************************/
16846 INT dm_task(void *pointer)
16847 /********************************************************************\
16848   Routine: dm_task
16849 
16850   Purpose: async send events doing a double purpose:
16851   a) send full buffer if found (DM_SEND) set by dm_active_full
16852   b) flush full areas (DM_FLUSH) set by dm_area_flush
16853   Input:
16854   none
16855   Output:
16856   none
16857   Function value:
16858   none
16859   \********************************************************************/
16860 {
16861 #ifdef DM_DUAL_THREAD
16862    INT status, timeout;
16863 
16864    printf("Semaphores initialization ... in areaSend ");
16865    /* Check or Wait for semaphore to be setup */
16866    timeout = ss_millitime();
16867    while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
16868       ss_sleep(200);
16869    if (dm.sem_send == 0)
16870       goto kill;
16871 
16872 #ifdef OS_WINNT
16873    /* necessary for true MUTEX (NT) get semaphore */
16874    ss_mutex_wait_for(dm.sem_flush, 0);
16875 #endif
16876 
16877    /* Main FOREVER LOOP */
16878    printf("task areaSend ready...\n");
16879    while (1) {
16880       if (!dm_area_full()) {
16881          /* wait semaphore here ........ 0 == forever */
16882          ss_mutex_wait_for(dm.sem_send, 0);
16883 #ifdef OS_WINNT
16884          /* necessary for true MUTEX (NT) give semaphore */
16885          ss_mutex_release(dm.sem_send);
16886 #endif
16887       }
16888       if (dm.action == DM_SEND) {
16889 #ifdef DM_DEBUG
16890          printf("Send %i %i ", dm.area1.full, dm.area2.full);
16891 #endif
16892          /* DM_SEND : Empty the oldest buffer only. */
16893          if (dm.area1.full && dm.area2.full) {
16894             if (dm.area1.serial <= dm.area2.serial)
16895                status = dm_buffer_send(&dm.area1);
16896             else
16897                status = dm_buffer_send(&dm.area2);
16898          } else if (dm.area1.full)
16899             status = dm_buffer_send(&dm.area1);
16900          else if (dm.area2.full)
16901             status = dm_buffer_send(&dm.area2);
16902 
16903          if (status != CM_SUCCESS) {
16904             cm_msg(MERROR, "dm_task", "network error %i", status);
16905             goto kill;
16906          }
16907       } /* if DM_SEND */
16908       else if (dm.action == DM_FLUSH) {
16909          /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
16910             events) Empty both area in order independently of being full or not */
16911          if (dm.area1.serial <= dm.area2.serial) {
16912             status = dm_buffer_send(&dm.area1);
16913             if (status != CM_SUCCESS)
16914                goto error;
16915             status = dm_buffer_send(&dm.area2);
16916             if (status != CM_SUCCESS)
16917                goto error;
16918          } else {
16919             status = dm_buffer_send(&dm.area2);
16920             if (status != CM_SUCCESS)
16921                goto error;
16922             status = dm_buffer_send(&dm.area1);
16923             if (status != CM_SUCCESS)
16924                goto error;
16925          }
16926          /* reset counter */
16927          dm.area1.serial = 0;
16928          dm.area2.serial = dm.serial = 1;
16929 #ifdef DM_DEBUG
16930          printf("dm.action: Flushing ...\n");
16931 #endif
16932          /* reset area to #1 */
16933          dm.pa = &dm.area1;
16934 
16935          /* release user */
16936          ss_mutex_release(dm.sem_flush);
16937 #ifdef OS_WINNT
16938          /* necessary for true MUTEX (NT) get semaphore back */
16939          ss_mutex_wait_for(dm.sem_flush, 0);
16940 #endif
16941       }
16942       /* if FLUSH */
16943       if (dm.action == DM_KILL)
16944          goto kill;
16945 
16946    }                            /* FOREVER (go back wainting for semaphore) */
16947 
16948    /* kill spawn now */
16949  error:
16950    cm_msg(MERROR, "dm_area_flush", "aSync Net error");
16951  kill:
16952    ss_mutex_release(dm.sem_flush);
16953 #ifdef OS_WINNT
16954    ss_mutex_wait_for(dm.sem_flush, 1);
16955 #endif
16956    cm_msg(MERROR, "areaSend", "task areaSend exiting now");
16957    exit;
16958    return 1;
16959 #else
16960    printf("DM_DUAL_THREAD not defined\n");
16961    return 0;
16962 #endif
16963 }
16964 
16965 /********************************************************************/
16966 INT dm_area_flush(void)
16967 /********************************************************************\
16968   Routine: dm_area_flush
16969 
16970   Purpose: Flush all the events in the areas.
16971            Used in mfe for BOR events, periodic events and
16972            if rate to low in main loop once a second. The standard
16973            data transfer should be done/triggered by dm_area_send (sync/async)
16974            in dm_pointer_get().
16975   Input:
16976     none
16977   Output:
16978     none
16979   Function value:
16980     CM_SUCCESS       Successful completion
16981     RPC_NET_ERROR    send error
16982 \********************************************************************/
16983 {
16984    INT status;
16985 #ifdef DM_DUAL_THREAD
16986    /* request FULL flush */
16987    dm.action = DM_FLUSH;
16988    ss_mutex_release(dm.sem_send);
16989 #ifdef OS_WINNT
16990    /* necessary for true MUTEX (NT) get semaphore back */
16991    ss_mutex_wait_for(dm.sem_send, 0);
16992 #endif
16993 
16994    /* important to wait for completion before continue with timeout
16995       timeout specified milliseconds */
16996    status = ss_mutex_wait_for(dm.sem_flush, 10000);
16997 #ifdef DM_DEBUG
16998    printf("dm_area_flush after waiting %i\n", status);
16999 #endif
17000 #ifdef OS_WINNT
17001    ss_mutex_release(dm.sem_flush);      /* give it back now */
17002 #endif
17003 
17004    return status;
17005 #else
17006    /* full flush done here */
17007    /* select in order both area independently of being full or not */
17008    if (dm.area1.serial <= dm.area2.serial) {
17009       status = dm_buffer_send(&dm.area1);
17010       if (status != CM_SUCCESS)
17011          return status;
17012       status = dm_buffer_send(&dm.area2);
17013       if (status != CM_SUCCESS)
17014          return status;
17015    } else {
17016       status = dm_buffer_send(&dm.area2);
17017       if (status != CM_SUCCESS)
17018          return status;
17019       status = dm_buffer_send(&dm.area1);
17020       if (status != CM_SUCCESS)
17021          return status;
17022    }
17023    /* reset serial counter */
17024    dm.area1.serial = dm.area2.serial = 0;
17025    dm.last_active = ss_millitime();
17026    return CM_SUCCESS;
17027 #endif
17028 }
17029 /**dox***************************************************************/
17030 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17031 
17032 /**dox***************************************************************/
17033 /** @} *//* end of dmfunctionc */
17034 
17035 
17036 /**dox***************************************************************/
17037 /** @addtogroup rbfunctionc
17038  *
17039  *  @{  */
17040 
17041 /**dox***************************************************************/
17042 #ifndef DOXYGEN_SHOULD_SKIP_THIS
17043 /********************************************************************/
17044 
17045 /********************************************************************\
17046 *                                                                    *
17047 *                 Ring buffer functions                              *
17048 *                                                                    *
17049 * Provide an inter-thread buffer scheme for handling front-end       *
17050 * events. This code allows concurrent data acquisition, calibration  *
17051 * and network transfer on a multi-CPU machine. One thread reads      *
17052 * out the data, passes it vis the ring buffer functions              *
17053 * to another thread running on the other CPU, which can then         *
17054 * calibrate and/or send the data over the network.                   *
17055 *                                                                    *
17056 \********************************************************************/
17057 
17058 typedef struct {
17059    unsigned char *buffer;
17060    unsigned int  size;
17061    unsigned int  max_event_size;
17062    unsigned char *rp;
17063    unsigned char *wp;
17064    unsigned char *ep;
17065 } RING_BUFFER;
17066 
17067 #define MAX_RING_BUFFER 10
17068 
17069 RING_BUFFER rb[MAX_RING_BUFFER];
17070 
17071 volatile int _rb_nonblocking = 0;
17072 
17073 /**dox***************************************************************/
17074 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17075 
17076 /********************************************************************/
17077 /**
17078 Set all rb_get_xx to nonblocking. Needed in multi-thread
17079 environments for stopping all theads without deadlock
17080 @return DB_SUCCESS
17081 
17082 */
17083 int rb_set_nonblocking()
17084 /********************************************************************\
17085 
17086   Routine: rb_set_nonblocking
17087 
17088   Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
17089            environments for stopping all theads without deadlock
17090 
17091   Input:
17092     NONE
17093 
17094   Output:
17095     NONE
17096 
17097   Function value:
17098     DB_SUCCESS       Successful completion
17099 
17100 \********************************************************************/
17101 {
17102    _rb_nonblocking = 1;
17103 
17104    return DB_SUCCESS;
17105 }
17106 
17107 /********************************************************************/
17108 /**
17109 Create a ring buffer with a given size
17110 
17111 Provide an inter-thread buffer scheme for handling front-end
17112 events. This code allows concurrent data acquisition, calibration
17113 and network transfer on a multi-CPU machine. One thread reads
17114 out the data, passes it via the ring buffer functions
17115 to another thread running on the other CPU, which can then
17116 calibrate and/or send the data over the network.
17117 
17118 @param size             Size of ring buffer, must be larger than
17119                          2*max_event_size
17120 @param max_event_size   Maximum event size to be placed into
17121 @param *handle          Handle to ring buffer
17122 @return DB_SUCCESS, DB_NO_MEMORY, DB_INVALID_PARAM
17123 */
17124 int rb_create(int size, int max_event_size, int *handle)
17125 /********************************************************************\
17126 
17127   Routine: rb_create
17128 
17129   Purpose: Create a ring buffer with a given size
17130 
17131   Input:
17132     int size             Size of ring buffer, must be larger than
17133                          2*max_event_size
17134     int max_event_size   Maximum event size to be placed into
17135                          ring buffer
17136   Output:
17137     int *handle          Handle to ring buffer
17138 
17139   Function value:
17140     DB_SUCCESS           Successful completion
17141     DB_NO_MEMORY         Maximum number of ring buffers exceeded
17142     DB_INVALID_PARAM     Invalid event size specified
17143 
17144 \********************************************************************/
17145 {
17146    int i;
17147 
17148    for (i=0 ; i<MAX_RING_BUFFER ; i++)
17149       if (rb[i].buffer == NULL)
17150          break;
17151 
17152    if (i == MAX_RING_BUFFER)
17153       return DB_NO_MEMORY;
17154 
17155    if (size < max_event_size * 2)
17156       return DB_INVALID_PARAM;
17157 
17158    memset(&rb[i], 0, sizeof(RING_BUFFER));
17159    rb[i].buffer = (unsigned char *) M_MALLOC(size);
17160    assert(rb[i].buffer);
17161    rb[i].size = size;
17162    rb[i].max_event_size = max_event_size;
17163    rb[i].rp = rb[i].buffer;
17164    rb[i].wp = rb[i].buffer;
17165    rb[i].ep = rb[i].buffer;
17166 
17167    *handle = i+1;
17168 
17169    return DB_SUCCESS;
17170 }
17171 
17172 /********************************************************************/
17173 /**
17174 Delete a ring buffer
17175 @param handle  Handle of the ring buffer
17176 @return  DB_SUCCESS
17177 */
17178 int rb_delete(int handle)
17179 /********************************************************************\
17180 
17181   Routine: rb_delete
17182 
17183   Purpose: Delete a ring buffer
17184 
17185   Input:
17186     none
17187   Output:
17188     int handle       Handle to ring buffer
17189 
17190   Function value:
17191     DB_SUCCESS       Successful completion
17192 
17193 \********************************************************************/
17194 {
17195    if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
17196       return DB_INVALID_HANDLE;
17197 
17198    M_FREE(rb[handle-1].buffer);
17199    memset(&rb[handle-1], 0, sizeof(RING_BUFFER));
17200 
17201    return DB_SUCCESS;
17202 }
17203 
17204 /********************************************************************/
17205 /**
17206 Retrieve write pointer where new data can be written
17207 @param handle               Ring buffer handle
17208 @param millisec             Optional timeout in milliseconds if
17209                               buffer is full. Zero to not wait at
17210                               all (non-blocking)
17211 @param  **p                  Write pointer
17212 @return DB_SUCCESS, DB_TIMEOUT, DB_INVALID_HANDLE
17213 */
17214 int rb_get_wp(int handle, void **p, int millisec)
17215 /********************************************************************\
17216 
17217 Routine: rb_get_wp
17218 
17219   Purpose: Retrieve write pointer where new data can be written
17220 
17221   Input:
17222      int handle               Ring buffer handle
17223      int millisec             Optional timeout in milliseconds if
17224                               buffer is full. Zero to not wait at
17225                               all (non-blocking)
17226 
17227   Output:
17228     char **p                  Write pointer
17229 
17230   Function value:
17231     DB_SUCCESS       Successful completion
17232 
17233 \********************************************************************/
17234 {
17235    int h, i;
17236    unsigned char *rp;
17237 
17238    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
17239       return DB_INVALID_HANDLE;
17240 
17241    h = handle - 1;
17242 
17243    for (i=0 ; i<=millisec/10 ; i++) {
17244 
17245       rp = rb[h].rp; // keep local copy, rb[h].rp might be changed by other thread
17246 
17247       /* check if enough size for wp >= rp without wrap-around */
17248       if (rb[h].wp >= rp &&
17249           rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17250          *p = rb[h].wp;
17251          return DB_SUCCESS;
17252       }
17253 
17254       /* check if enough size for wp >= rp with wrap-around */
17255       if (rb[h].wp >= rp &&
17256           rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
17257           rb[h].rp > rb[h].buffer) {  // next increment of wp wraps around, so need space at beginning
17258          *p = rb[h].wp;
17259          return DB_SUCCESS;
17260       }
17261 
17262       /* check if enough size for wp < rp */
17263       if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
17264          *p = rb[h].wp;
17265          return DB_SUCCESS;
17266       }
17267 
17268       if (millisec == 0)
17269          return DB_TIMEOUT;
17270 
17271       if (_rb_nonblocking)
17272          return DB_TIMEOUT;
17273 
17274       /* wait one time slice */
17275       ss_sleep(10);
17276    }
17277 
17278    return DB_TIMEOUT;
17279 }
17280 
17281 /********************************************************************/
17282 /** rb_increment_wp
17283 
17284 Increment current write pointer, making the data at
17285 the write pointer available to the receiving thread
17286 @param handle               Ring buffer handle
17287 @param size                 Number of bytes placed at the WP
17288 @return DB_SUCCESS, DB_INVALID_PARAM, DB_INVALID_HANDLE
17289 */
17290 int rb_increment_wp(int handle, int size)
17291 /********************************************************************\
17292 
17293   Routine: rb_increment_wp
17294 
17295   Purpose: Increment current write pointer, making the data at
17296            the write pointer available to the receiving thread
17297 
17298   Input:
17299      int handle               Ring buffer handle
17300      int size                 Number of bytes placed at the WP
17301 
17302   Output:
17303     NONE
17304 
17305   Function value:
17306     DB_SUCCESS                Successful completion
17307     DB_INVALID_PARAM          Event size too large or invalid handle
17308 \********************************************************************/
17309 {
17310    int h;
17311    unsigned char *old_wp, *new_wp;
17312 
17313    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
17314       return DB_INVALID_HANDLE;
17315 
17316    h = handle - 1;
17317 
17318    if ((DWORD) size > rb[h].max_event_size)
17319       return DB_INVALID_PARAM;
17320 
17321    old_wp = rb[h].wp;
17322    new_wp = rb[h].wp + size;
17323 
17324    /* wrap around wp if not enough space */
17325    if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17326       rb[h].ep = new_wp;
17327       new_wp = rb[h].buffer;
17328       assert(rb[h].rp != rb[h].buffer);
17329    }
17330 
17331    rb[h].wp = new_wp;
17332 
17333    return DB_SUCCESS;
17334 }
17335 
17336 /********************************************************************/
17337 /**
17338 Obtain the current read pointer at which new data is
17339 available with optional timeout
17340 
17341 @param  handle               Ring buffer handle
17342 @param  millisec             Optional timeout in milliseconds if
17343                              buffer is full. Zero to not wait at
17344                              all (non-blocking)
17345 
17346 @param **p                 Address of pointer pointing to newly
17347                              available data. If p == NULL, only
17348                              return status.
17349 @return  DB_SUCCESS, DB_TIEMOUT, DB_INVALID_HANDLE
17350 
17351 */
17352 int rb_get_rp(int handle, void **p, int millisec)
17353 /********************************************************************\
17354 
17355   Routine: rb_get_rp
17356 
17357   Purpose: Obtain the current read pointer at which new data is
17358            available with optional timeout
17359 
17360   Input:
17361     int handle               Ring buffer handle
17362     int millisec             Optional timeout in milliseconds if
17363                              buffer is full. Zero to not wait at
17364                              all (non-blocking)
17365 
17366   Output:
17367     char **p                 Address of pointer pointing to newly
17368                              available data. If p == NULL, only
17369                              return status.
17370 
17371   Function value:
17372     DB_SUCCESS       Successful completion
17373 
17374 \********************************************************************/
17375 {
17376    int i, h;
17377 
17378    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
17379       return DB_INVALID_HANDLE;
17380 
17381    h = handle - 1;
17382 
17383    for (i=0 ; i <= millisec/10 ; i++) {
17384 
17385       if (rb[h].wp != rb[h].rp) {
17386          if (p != NULL)
17387             *p = rb[handle-1].rp;
17388          return DB_SUCCESS;
17389       }
17390 
17391       if (millisec == 0)
17392          return DB_TIMEOUT;
17393 
17394       if (_rb_nonblocking)
17395          return DB_TIMEOUT;
17396 
17397       /* wait one time slice */
17398       ss_sleep(10);
17399    }
17400 
17401    return DB_TIMEOUT;
17402 }
17403 
17404 /********************************************************************/
17405 /**
17406 Increment current read pointer, freeing up space for the writing thread.
17407 
17408 @param handle               Ring buffer handle
17409 @param size                 Number of bytes to free up at current
17410                               read pointer
17411 @return  DB_SUCCESS, DB_INVALID_PARAM
17412 
17413 */
17414 int rb_increment_rp(int handle, int size)
17415 /********************************************************************\
17416 
17417   Routine: rb_increment_rp
17418 
17419   Purpose: Increment current read pointer, freeing up space for
17420            the writing thread.
17421 
17422   Input:
17423      int handle               Ring buffer handle
17424      int size                 Number of bytes to free up at current
17425                               read pointer
17426 
17427   Output:
17428     NONE
17429 
17430   Function value:
17431     DB_SUCCESS                Successful completion
17432     DB_INVALID_PARAM          Event size too large or invalid handle
17433 
17434 \********************************************************************/
17435 {
17436    int h;
17437 
17438    unsigned char *new_rp, *old_rp;
17439 
17440    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
17441       return DB_INVALID_HANDLE;
17442 
17443    h = handle - 1;
17444 
17445    if ((DWORD) size > rb[h].max_event_size)
17446       return DB_INVALID_PARAM;
17447 
17448    old_rp = rb[h].rp;
17449    new_rp = rb[h].rp + size;
17450 
17451    /* wrap around if not enough space left */
17452    if (new_rp + rb[h].max_event_size > rb[h].buffer + rb[h].size)
17453       new_rp = rb[h].buffer;
17454 
17455    rb[handle-1].rp = new_rp;
17456 
17457    return DB_SUCCESS;
17458 }
17459 
17460 /********************************************************************/
17461 /**
17462 Return number of bytes in a ring buffer
17463 
17464 @param handle              Handle of the buffer to get the info
17465 @param *n_bytes            Number of bytes in buffer
17466 @return DB_SUCCESS, DB_INVALID_HANDLE
17467 */
17468 int rb_get_buffer_level(int handle, int * n_bytes)
17469 /********************************************************************\
17470 
17471   Routine: rb_get_buffer_level
17472 
17473   Purpose: Return number of bytes in a ring buffer
17474 
17475   Input:
17476     int handle              Handle of the buffer to get the info
17477 
17478   Output:
17479     int *n_bytes            Number of bytes in buffer
17480 
17481   Function value:
17482     DB_SUCCESS              Successful completion
17483     DB_INVALID_HANDLE       Buffer handle is invalid
17484 
17485 \********************************************************************/
17486 {
17487    int h;
17488 
17489    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
17490       return DB_INVALID_HANDLE;
17491 
17492    h = handle - 1;
17493 
17494    if (rb[h].wp >= rb[h].rp)
17495       *n_bytes = (POINTER_T)rb[h].wp - (POINTER_T)rb[h].rp;
17496    else
17497       *n_bytes = (POINTER_T)rb[h].ep - (POINTER_T)rb[h].rp + (POINTER_T)rb[h].wp - (POINTER_T)rb[h].buffer;
17498 
17499    return DB_SUCCESS;
17500 }
17501 
17502 /**dox***************************************************************/
17503 /** @} *//* end of rbfunctionc */
17504 
17505 /**dox***************************************************************/
17506 /** @} *//* end of midasincludecode */

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