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 4213 2008-06-05 19:17:22Z ritt@PSI.CH $
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: 4213 $";
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 rpcfunctionc Midas RPC Functions (rpc_xxx)
00037  */
00038 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00039  */
00040 /** @defgroup rbfunctionc Midas Ring Buffer Functions (rb_xxx)
00041  */
00042 
00043 /**dox***************************************************************/
00044 /** @addtogroup midasincludecode
00045  *
00046  *  @{  */
00047 
00048 /**dox***************************************************************/
00049 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00050 
00051 /********************************************************************/
00052 /* data type sizes */
00053 INT tid_size[] = {
00054    0,                           /* tid == 0 not defined                               */
00055    1,                           /* TID_BYTE      unsigned byte         0       255    */
00056    1,                           /* TID_SBYTE     signed byte         -128      127    */
00057    1,                           /* TID_CHAR      single character      0       255    */
00058    2,                           /* TID_WORD      two bytes             0      65535   */
00059    2,                           /* TID_SHORT     signed word        -32768    32767   */
00060    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00061    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00062    4,                           /* TID_BOOL      four bytes bool       0        1     */
00063    4,                           /* TID_FLOAT     4 Byte float format                  */
00064    8,                           /* TID_DOUBLE    8 Byte float format                  */
00065    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00066    0,                           /* TID_STRING    zero terminated string               */
00067    0,                           /* TID_ARRAY     variable length array of unkown type */
00068    0,                           /* TID_STRUCT    C structure                          */
00069    0,                           /* TID_KEY       key in online database               */
00070    0                            /* TID_LINK      link in online database              */
00071 };
00072 
00073 /* data type names */
00074 char *tid_name[] = {
00075    "NULL",
00076    "BYTE",
00077    "SBYTE",
00078    "CHAR",
00079    "WORD",
00080    "SHORT",
00081    "DWORD",
00082    "INT",
00083    "BOOL",
00084    "FLOAT",
00085    "DOUBLE",
00086    "BITFIELD",
00087    "STRING",
00088    "ARRAY",
00089    "STRUCT",
00090    "KEY",
00091    "LINK"
00092 };
00093 
00094 struct {
00095    int transition;
00096    char name[32];
00097 } trans_name[] = {
00098    {
00099    TR_START, "START",}, {
00100    TR_STOP, "STOP",}, {
00101    TR_PAUSE, "PAUSE",}, {
00102    TR_RESUME, "RESUME",}, {
00103    TR_DEFERRED, "DEFERRED",}, {
00104 0, "",},};
00105 
00106 /* Globals */
00107 #ifdef OS_MSDOS
00108 extern unsigned _stklen = 60000U;
00109 #endif
00110 
00111 extern DATABASE *_database;
00112 extern INT _database_entries;
00113 
00114 static BUFFER *_buffer;
00115 static INT _buffer_entries = 0;
00116 
00117 static INT _msg_buffer = 0;
00118 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00119 
00120 static REQUEST_LIST *_request_list;
00121 static INT _request_list_entries = 0;
00122 
00123 static EVENT_HEADER *_event_buffer;
00124 static INT _event_buffer_size = 0;
00125 
00126 static char *_net_recv_buffer;
00127 static INT _net_recv_buffer_size = 0;
00128 
00129 static char *_net_send_buffer;
00130 static INT _net_send_buffer_size = 0;
00131 
00132 static char *_tcp_buffer = NULL;
00133 static INT _tcp_wp = 0;
00134 static INT _tcp_rp = 0;
00135 static INT _rpc_sock = 0;
00136 
00137 static INT _send_sock;
00138 
00139 static void (*_debug_print) (char *) = NULL;
00140 static INT _debug_mode = 0;
00141 
00142 static INT _watchdog_last_called = 0;
00143 
00144 /* table for transition functions */
00145 
00146 typedef struct {
00147    INT transition;
00148    INT sequence_number;
00149     INT(*func) (INT, char *);
00150 } TRANS_TABLE;
00151 
00152 #define MAX_TRANSITIONS 20
00153 
00154 TRANS_TABLE _trans_table[MAX_TRANSITIONS];
00155 
00156 TRANS_TABLE _deferred_trans_table[] = {
00157    {TR_START,0,NULL},
00158    {TR_STOP,0,NULL},
00159    {TR_PAUSE,0,NULL},
00160    {TR_RESUME,0,NULL},
00161    {0,0,NULL}
00162 };
00163 
00164 static BOOL _server_registered = FALSE;
00165 
00166 static INT rpc_transition_dispatch(INT idx, void *prpc_param[]);
00167 
00168 void cm_ctrlc_handler(int sig);
00169 
00170 typedef struct {
00171    INT code;
00172    char *string;
00173 } ERROR_TABLE;
00174 
00175 ERROR_TABLE _error_table[] = {
00176    {CM_WRONG_PASSWORD, "Wrong password"},
00177    {CM_UNDEF_EXP, "Experiment not defined"},
00178    {CM_UNDEF_ENVIRON,
00179     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00180    {RPC_NET_ERROR, "Cannot connect to remote host"},
00181    {0, NULL}
00182 };
00183 
00184 
00185 typedef struct {
00186    void *adr;
00187    int size;
00188    char file[80];
00189    int line;
00190 } DBG_MEM_LOC;
00191 
00192 DBG_MEM_LOC *_mem_loc = NULL;
00193 INT _n_mem = 0;
00194 
00195 void *dbg_malloc(unsigned int size, char *file, int line)
00196 {
00197    FILE *f;
00198    void *adr;
00199    int i;
00200 
00201    adr = malloc(size);
00202 
00203    /* search for deleted entry */
00204    for (i = 0; i < _n_mem; i++)
00205       if (_mem_loc[i].adr == NULL)
00206          break;
00207 
00208    if (i == _n_mem) {
00209       _n_mem++;
00210       if (!_mem_loc)
00211          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00212       else
00213          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00214    }
00215 
00216    _mem_loc[i].adr = adr;
00217    _mem_loc[i].size = size;
00218    strcpy(_mem_loc[i].file, file);
00219    _mem_loc[i].line = line;
00220 
00221    f = fopen("mem.txt", "w");
00222    for (i = 0; i < _n_mem; i++)
00223       if (_mem_loc[i].adr)
00224          fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
00225                  _mem_loc[i].size, _mem_loc[i].adr);
00226    fclose(f);
00227 
00228    return adr;
00229 }
00230 
00231 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00232 {
00233    void *adr;
00234 
00235    adr = dbg_malloc(size * count, file, line);
00236    if (adr)
00237       memset(adr, 0, size * count);
00238 
00239    return adr;
00240 }
00241 
00242 void dbg_free(void *adr, char *file, int line)
00243 {
00244    FILE *f;
00245    int i;
00246 
00247    free(adr);
00248 
00249    for (i = 0; i < _n_mem; i++)
00250       if (_mem_loc[i].adr == adr)
00251          break;
00252 
00253    if (i < _n_mem)
00254       _mem_loc[i].adr = NULL;
00255 
00256    f = fopen("mem.txt", "w");
00257    for (i = 0; i < _n_mem; i++)
00258       if (_mem_loc[i].adr)
00259          fprintf(f, "%s:%d %s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line, 
00260                  file, line, _mem_loc[i].size, _mem_loc[i].adr);
00261    fclose(f);
00262 }
00263 
00264 /********************************************************************\
00265 *                                                                    *
00266 *              Common message functions                              *
00267 *                                                                    *
00268 \********************************************************************/
00269 
00270 static int (*_message_print) (const char *) = puts;
00271 static INT _message_mask_system = MT_ALL;
00272 static INT _message_mask_user = MT_ALL;
00273 
00274 
00275 /**dox***************************************************************/
00276 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00277 
00278 /**dox***************************************************************/
00279 /** @addtogroup msgfunctionc
00280  *
00281  *  @{  */
00282 
00283 /********************************************************************/
00284 /**
00285 Convert error code to string. Used after cm_connect_experiment to print
00286 error string in command line programs or windows programs.
00287 @param code Error code as defined in midas.h
00288 @param string Error string
00289 @return CM_SUCCESS
00290 */
00291 INT cm_get_error(INT code, char *string)
00292 {
00293    INT i;
00294 
00295    for (i = 0; _error_table[i].code; i++)
00296       if (_error_table[i].code == code) {
00297          strcpy(string, _error_table[i].string);
00298          return CM_SUCCESS;
00299       }
00300 
00301    sprintf(string, "Unexpected error #%d", code);
00302    return CM_SUCCESS;
00303 }
00304 
00305 /********************************************************************/
00306 /**
00307 Set message masks. When a message is generated by calling cm_msg(),
00308 it can got to two destinatinons. First a user defined callback routine
00309 and second to the "SYSMSG" buffer.
00310 
00311 A user defined callback receives all messages which satisfy the user_mask.
00312 
00313 \code
00314 int message_print(const char *msg)
00315 {
00316   char str[160];
00317 
00318   memset(str, ' ', 159);
00319   str[159] = 0;
00320   if (msg[0] == '[')
00321     msg = strchr(msg, ']')+2;
00322   memcpy(str, msg, strlen(msg));
00323   ss_printf(0, 20, str);
00324   return 0;
00325 }
00326 ...
00327   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
00328 ...
00329 \endcode
00330 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
00331 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
00332 @param func Function which receives all printout. By setting "puts",
00333        messages are just printed to the screen.
00334 @return CM_SUCCESS
00335 */
00336 INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func) (const char *))
00337 {
00338    _message_mask_system = system_mask;
00339    _message_mask_user = user_mask;
00340    _message_print = func;
00341 
00342    return BM_SUCCESS;
00343 }
00344 
00345 /********************************************************************/
00346 /**
00347 Write message to logging file. Called by cm_msg.
00348 @attention May burn your fingers
00349 @param message_type      Message type
00350 @param message          Message string
00351 @return CM_SUCCESS
00352 */
00353 INT cm_msg_log(INT message_type, const char *message)
00354 {
00355    char dir[256];
00356    char filename[256];
00357    char path[256];
00358    char str[256];
00359    INT status, size, fh, mutex;
00360    HNDLE hDB, hKey;
00361 
00362    if (rpc_is_remote())
00363       return rpc_call(RPC_CM_MSG_LOG, message_type, message);
00364 
00365    if (message_type != MT_DEBUG) {
00366       cm_get_experiment_database(&hDB, NULL);
00367 
00368       if (hDB) {
00369 
00370          strcpy(filename, "midas.log");
00371          size = sizeof(filename);
00372          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00373 
00374          if (strchr(filename, '%')) {
00375             /* replace stings such as midas_%y%m%d.mid with current date */
00376             time_t now;
00377             struct tm *tms;
00378 
00379             tzset();
00380             time(&now);
00381             tms = localtime(&now);
00382 
00383             strftime(str, sizeof(str), filename, tms);
00384             strlcpy(filename, str, sizeof(filename));
00385          }
00386 
00387          if (strchr(filename, DIR_SEPARATOR) == NULL) {
00388             status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00389             if (status == DB_SUCCESS) {
00390                size = sizeof(dir);
00391                memset(dir, 0, size);
00392                db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
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, filename);
00399             } else {
00400                cm_get_path(dir);
00401                if (dir[0] != 0)
00402                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00403                      strcat(dir, DIR_SEPARATOR_STR);
00404 
00405                strcpy(path, dir);
00406                strcat(path, "midas.log");
00407             }
00408          } else {
00409             strcpy(path, filename);
00410          }
00411       } else
00412          strcpy(path, "midas.log");
00413 
00414       fh = open(path, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
00415       if (fh < 0) {
00416          printf("Cannot open message log file %s\n", path);
00417       } else {
00418          
00419          cm_get_experiment_mutex(NULL, NULL, NULL, &mutex);
00420          status = ss_mutex_wait_for(mutex, 5 * 1000);
00421 
00422          strcpy(str, ss_asctime());
00423          write(fh, str, strlen(str));
00424          write(fh, " ", 1);
00425          write(fh, message, strlen(message));
00426          write(fh, "\n", 1);
00427          close(fh);
00428 
00429          ss_mutex_release(mutex);
00430       }
00431    }
00432 
00433    return CM_SUCCESS;
00434 }
00435 
00436 /********************************************************************/
00437 /**
00438 Write message to logging file. Called by cm_msg().
00439 @internal
00440 @param message_type      Message type
00441 @param message          Message string
00442 @param facility         Message facility, filename in which messages will be written
00443 @return CM_SUCCESS
00444 */
00445 INT cm_msg_log1(INT message_type, const char *message, const char *facility)
00446 /********************************************************************\
00447 
00448   Routine: cm_msg_log1
00449 
00450   Purpose: Write message to logging file. Called by cm_msg.
00451            Internal use only
00452 
00453   Input:
00454     INT    message_type      Message type
00455     char   *message          Message string
00456     char   *
00457 
00458   Output:
00459     none
00460 
00461   Function value:
00462     CM_SUCCESS
00463 
00464 \********************************************************************/
00465 {
00466    char dir[256];
00467    char filename[256];
00468    char path[256];
00469    char str[256];
00470    FILE *f;
00471    INT status, size;
00472    HNDLE hDB, hKey;
00473 
00474 
00475    if (rpc_is_remote())
00476       return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
00477 
00478    if (message_type != MT_DEBUG) {
00479       cm_get_experiment_database(&hDB, NULL);
00480 
00481       if (hDB) {
00482          strcpy(filename, "midas.log");
00483          size = sizeof(filename);
00484          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00485 
00486          if (strchr(filename, '%')) {
00487             /* replace stings such as midas_%y%m%d.mid with current date */
00488             time_t now;
00489             struct tm *tms;
00490 
00491             tzset();
00492             time(&now);
00493             tms = localtime(&now);
00494 
00495             strftime(str, sizeof(str), filename, tms);
00496             strlcpy(filename, str, sizeof(filename));
00497          }
00498 
00499          if (strchr(filename, DIR_SEPARATOR) == NULL) {
00500 
00501             status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00502             if (status == DB_SUCCESS) {
00503                size = sizeof(dir);
00504                memset(dir, 0, size);
00505                db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00506                if (dir[0] != 0)
00507                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00508                      strcat(dir, DIR_SEPARATOR_STR);
00509 
00510                if (facility[0]) {
00511                   strcpy(filename, facility);
00512                   strcat(filename, ".log");
00513                } else {
00514                   strcpy(filename, "midas.log");
00515                   size = sizeof(filename);
00516                   db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00517 
00518                   if (strchr(filename, '%')) {
00519                      /* replace stings such as midas_%y%m%d.mid with current date */
00520                      time_t now;
00521                      struct tm *tms;
00522 
00523                      tzset();
00524                      time(&now);
00525                      tms = localtime(&now);
00526 
00527                      strftime(str, sizeof(str), filename, tms);
00528                      strlcpy(filename, str, sizeof(filename));
00529                   }
00530                }
00531 
00532                strcpy(path, dir);
00533                strcat(path, filename);
00534             } else {
00535                cm_get_path(dir);
00536                if (dir[0] != 0)
00537                   if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00538                      strcat(dir, DIR_SEPARATOR_STR);
00539 
00540                strcpy(path, dir);
00541                if (facility[0]) {
00542                   strcat(path, facility);
00543                   strcat(path, ".log");
00544                } else
00545                   strcat(path, "midas.log");
00546             }
00547          } else {
00548             strcpy(path, filename);
00549             *(strrchr(path, DIR_SEPARATOR) + 1) = 0;
00550             if (facility[0]) {
00551                strcat(path, facility);
00552                strcat(path, ".log");
00553             } else
00554                strcat(path, "midas.log");
00555          }
00556       } else {
00557          if (facility[0]) {
00558             strcpy(path, facility);
00559             strcat(path, ".log");
00560          } else
00561             strcpy(path, "midas.log");
00562       }
00563 
00564       f = fopen(path, "a");
00565       if (f == NULL) {
00566          printf("Cannot open message log file %s\n", path);
00567       } else {
00568          strcpy(str, ss_asctime());
00569          fprintf(f, str);
00570          fprintf(f, " %s\n", message);
00571          fclose(f);
00572       }
00573    }
00574 
00575    return CM_SUCCESS;
00576 }
00577 
00578 /********************************************************************/
00579 /**
00580 This routine can be called whenever an internal error occurs
00581 or an informative message is produced. Different message
00582 types can be enabled or disabled by setting the type bits
00583 via cm_set_msg_print().
00584 @attention Do not add the "\n" escape carriage control at the end of the
00585 formated line as it is already added by the client on the receiving side.
00586 \code
00587    ...
00588    cm_msg(MINFO, "my program", "This is a information message only);
00589    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
00590    cm_msg(MTALK, "my_program", My program is Done!");
00591    ...
00592 \endcode
00593 @param message_type (See @ref midas_macro).
00594 @param filename Name of source file where error occured
00595 @param line Line number where error occured
00596 @param routine Routine name.
00597 @param format message to printout, ... Parameters like for printf()
00598 @return CM_SUCCESS
00599 */
00600 INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
00601 {
00602    va_list argptr;
00603    char event[1000], type_str[256], str[1000], format_cpy[900], local_message[1000], send_message[1000];
00604    const char *pc;
00605    EVENT_HEADER *pevent;
00606    INT status;
00607    static BOOL in_routine = FALSE;
00608 
00609    /* avoid recursive calls */
00610    if (in_routine)
00611       return 0;
00612 
00613    in_routine = TRUE;
00614 
00615    /* strip path */
00616    pc = filename + strlen(filename);
00617    while (*pc != '\\' && *pc != '/' && pc != filename)
00618       pc--;
00619    if (pc != filename)
00620       pc++;
00621 
00622    /* convert type to string */
00623    type_str[0] = 0;
00624    if (message_type & MT_ERROR)
00625       strlcat(type_str, MT_ERROR_STR, sizeof(type_str));
00626    if (message_type & MT_INFO)
00627       strlcat(type_str, MT_INFO_STR, sizeof(type_str));
00628    if (message_type & MT_DEBUG)
00629       strlcat(type_str, MT_DEBUG_STR, sizeof(type_str));
00630    if (message_type & MT_USER)
00631       strlcat(type_str, MT_USER_STR, sizeof(type_str));
00632    if (message_type & MT_LOG)
00633       strlcat(type_str, MT_LOG_STR, sizeof(type_str));
00634    if (message_type & MT_TALK)
00635       strlcat(type_str, MT_TALK_STR, sizeof(type_str));
00636 
00637    /* print client name into string */
00638    if (message_type == MT_USER)
00639       sprintf(send_message, "[%s] ", routine);
00640    else {
00641       rpc_get_name(str);
00642       if (str[0])
00643          sprintf(send_message, "[%s,%s] ", str, type_str);
00644       else
00645          send_message[0] = 0;
00646    }
00647 
00648    local_message[0] = 0;
00649 
00650    /* preceed error messages with file and line info */
00651    if (message_type == MT_ERROR) {
00652       sprintf(str, "[%s:%d:%s,%s] ", pc, line, routine, type_str);
00653       strlcat(send_message, str, sizeof(send_message));
00654       strlcat(local_message, str, sizeof(send_message));
00655    } else if (message_type == MT_USER)
00656       sprintf(local_message, "[%s,%s] ", routine, type_str);
00657 
00658    /* limit length of format */
00659    strlcpy(format_cpy, format, sizeof(format_cpy));
00660 
00661    /* print argument list into message */
00662    va_start(argptr, format);
00663    vsprintf(str, (char *) format, argptr);
00664    va_end(argptr);
00665    strcat(send_message, str);
00666    strcat(local_message, str);
00667 
00668    /* call user function if set via cm_set_msg_print */
00669    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00670       _message_print(local_message);
00671 
00672    /* return if system mask is not set */
00673    if ((message_type & _message_mask_system) == 0) {
00674       in_routine = FALSE;
00675       return CM_SUCCESS;
00676    }
00677 
00678    /* copy message to event */
00679    pevent = (EVENT_HEADER *) event;
00680    strcpy(event + sizeof(EVENT_HEADER), send_message);
00681 
00682    /* send event if not of type MLOG */
00683    if (message_type != MT_LOG) {
00684       /* if no message buffer already opened, do so now */
00685       if (_msg_buffer == 0) {
00686          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00687          if (status != BM_SUCCESS && status != BM_CREATED) {
00688             in_routine = FALSE;
00689             return status;
00690          }
00691       }
00692 
00693       /* setup the event header and send the message */
00694       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00695                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00696       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00697    }
00698 
00699    /* log message */
00700    cm_msg_log(message_type, send_message);
00701 
00702    in_routine = FALSE;
00703 
00704    return CM_SUCCESS;
00705 }
00706 
00707 /********************************************************************/
00708 /**
00709 This routine is similar to @ref cm_msg().
00710 It differs from cm_msg() only by the logging destination being a file
00711 given through the argument list i.e:\b facility
00712 @internal
00713 @attention Do not add the "\n" escape carriage control at the end of the
00714 formated line as it is already added by the client on the receiving side.
00715 The first arg in the following example uses the predefined
00716 macro MINFO which handles automatically the first 3 arguments of the function
00717 (see @ref midas_macro).
00718 \code   ...
00719    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
00720    ...
00721 //----- File my_log_file.log
00722 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
00723 \endcode
00724 @param message_type See @ref midas_macro.
00725 @param filename Name of source file where error occured
00726 @param line Line number where error occured
00727 @param facility Logging file name
00728 @param routine Routine name
00729 @param format message to printout, ... Parameters like for printf()
00730 @return CM_SUCCESS
00731 */
00732 INT cm_msg1(INT message_type, const char *filename, INT line,
00733             const char *facility, const char *routine, const char *format, ...)
00734 {
00735    va_list argptr;
00736    char event[1000], str[256], local_message[256], send_message[256];
00737    const char *pc;
00738    EVENT_HEADER *pevent;
00739    INT status;
00740    static BOOL in_routine = FALSE;
00741 
00742    /* avoid recursive calles */
00743    if (in_routine)
00744       return 0;
00745 
00746    in_routine = TRUE;
00747 
00748    /* strip path */
00749    pc = filename + strlen(filename);
00750    while (*pc != '\\' && *pc != '/' && pc != filename)
00751       pc--;
00752    if (pc != filename)
00753       pc++;
00754 
00755    /* print client name into string */
00756    if (message_type == MT_USER)
00757       sprintf(send_message, "[%s] ", routine);
00758    else {
00759       rpc_get_name(str);
00760       if (str[0])
00761          sprintf(send_message, "[%s] ", str);
00762       else
00763          send_message[0] = 0;
00764    }
00765 
00766    local_message[0] = 0;
00767 
00768    /* preceed error messages with file and line info */
00769    if (message_type == MT_ERROR) {
00770       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
00771       strcat(send_message, str);
00772       strcat(local_message, str);
00773    }
00774 
00775    /* print argument list into message */
00776    va_start(argptr, format);
00777    vsprintf(str, (char *) format, argptr);
00778    va_end(argptr);
00779 
00780    if (facility)
00781       sprintf(local_message + strlen(local_message), "{%s} ", facility);
00782 
00783    strcat(send_message, str);
00784    strcat(local_message, str);
00785 
00786    /* call user function if set via cm_set_msg_print */
00787    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
00788       _message_print(local_message);
00789 
00790    /* return if system mask is not set */
00791    if ((message_type & _message_mask_system) == 0) {
00792       in_routine = FALSE;
00793       return CM_SUCCESS;
00794    }
00795 
00796    /* copy message to event */
00797    pevent = (EVENT_HEADER *) event;
00798    strcpy(event + sizeof(EVENT_HEADER), send_message);
00799 
00800    /* send event if not of type MLOG */
00801    if (message_type != MT_LOG) {
00802       /* if no message buffer already opened, do so now */
00803       if (_msg_buffer == 0) {
00804          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00805          if (status != BM_SUCCESS && status != BM_CREATED) {
00806             in_routine = FALSE;
00807             return status;
00808          }
00809       }
00810 
00811       /* setup the event header and send the message */
00812       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
00813                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
00814       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
00815    }
00816 
00817    /* log message */
00818    cm_msg_log1(message_type, send_message, facility);
00819 
00820    in_routine = FALSE;
00821 
00822    return CM_SUCCESS;
00823 }
00824 
00825 /********************************************************************/
00826 /**
00827 Register a dispatch function for receiving system messages.
00828 - example code from mlxspeaker.c
00829 \code
00830 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
00831 {
00832   char str[256], *pc, *sp;
00833   // print message
00834   printf("%s\n", (char *)(message));
00835 
00836   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
00837                  ,header->event_id
00838                  ,header->trigger_mask
00839                  ,header->serial_number
00840                  ,header->data_size);
00841   pc = strchr((char *)(message),']')+2;
00842   ...
00843   // skip none talking message
00844   if (header->trigger_mask == MT_TALK ||
00845       header->trigger_mask == MT_USER)
00846    ...
00847 }
00848 
00849 int main(int argc, char *argv[])
00850 {
00851   ...
00852   // now connect to server
00853   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
00854   if (status != CM_SUCCESS)
00855     return 1;
00856   // Register callback for messages
00857   cm_msg_register(receive_message);
00858   ...
00859 }
00860 \endcode
00861 @param func Dispatch function.
00862 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
00863 */
00864 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
00865 {
00866    INT status, id;
00867 
00868    /* if no message buffer already opened, do so now */
00869    if (_msg_buffer == 0) {
00870       status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
00871       if (status != BM_SUCCESS && status != BM_CREATED)
00872          return status;
00873    }
00874 
00875    _msg_dispatch = func;
00876 
00877    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_SOME, &id, func);
00878 
00879    return status;
00880 }
00881 
00882 /********************************************************************/
00883 /**
00884 Retrieve old messages from log file
00885 @param  n_message        Number of messages to retrieve
00886 @param  message          buf_size bytes of messages, separated
00887                          by \n characters. The returned number
00888                          of bytes is normally smaller than the
00889                          initial buf_size, since only full
00890                          lines are returned.
00891 @param *buf_size         Size of message buffer to fill
00892 @return CM_SUCCESS
00893 */
00894 INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
00895 {
00896    char dir[256], str[256];
00897    char filename[256];
00898    char path[256], *p;
00899    FILE *f;
00900    INT status, size, offset, i;
00901    HNDLE hDB, hKey;
00902 
00903 
00904    if (rpc_is_remote())
00905       return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
00906 
00907    cm_get_experiment_database(&hDB, NULL);
00908 
00909    if (hDB) {
00910       strcpy(filename, "midas.log");
00911       size = sizeof(filename);
00912       db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
00913 
00914       if (strchr(filename, '%')) {
00915          /* replace stings such as midas_%y%m%d.mid with current date */
00916          time_t now;
00917          struct tm *tms;
00918 
00919          tzset();
00920          time(&now);
00921          tms = localtime(&now);
00922 
00923          strftime(str, sizeof(str), filename, tms);
00924          strlcpy(filename, str, sizeof(filename));
00925       }
00926 
00927       if (strchr(filename, DIR_SEPARATOR) == NULL) {
00928          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
00929          if (status == DB_SUCCESS) {
00930             size = sizeof(dir);
00931             memset(dir, 0, size);
00932             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
00933             if (dir[0] != 0)
00934                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00935                   strcat(dir, DIR_SEPARATOR_STR);
00936 
00937             strcpy(path, dir);
00938             strcat(path, filename);
00939          } else {
00940             cm_get_path(dir);
00941             if (dir[0] != 0)
00942                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
00943                   strcat(dir, DIR_SEPARATOR_STR);
00944 
00945             strcpy(path, dir);
00946             strcat(path, filename);
00947          }
00948       } else {
00949          strcpy(path, filename);
00950       }
00951    } else
00952       strcpy(path, "midas.log");
00953 
00954    f = fopen(path, "rb");
00955    if (f == NULL) {
00956       sprintf(message, "Cannot open message log file %s\n", path);
00957       return CM_DB_ERROR;
00958    } else {
00959       /* position buf_size bytes before the EOF */
00960       fseek(f, -(buf_size - 1), SEEK_END);
00961       offset = ftell(f);
00962       if (offset != 0) {
00963          /* go to end of line */
00964          fgets(message, buf_size - 1, f);
00965          offset = ftell(f) - offset;
00966          buf_size -= offset;
00967       }
00968 
00969       memset(message, 0, buf_size);
00970       fread(message, 1, buf_size - 1, f);
00971       message[buf_size - 1] = 0;
00972       fclose(f);
00973 
00974       p = message + (buf_size - 2);
00975 
00976       /* goto end of buffer */
00977       while (p != message && *p == 0)
00978          p--;
00979 
00980       /* strip line break */
00981       while (p != message && (*p == '\n' || *p == '\r'))
00982          *(p--) = 0;
00983 
00984       /* trim buffer so that last n_messages remain */
00985       for (i = 0; i < n_message; i++) {
00986          while (p != message && *p != '\n')
00987             p--;
00988 
00989          while (p != message && (*p == '\n' || *p == '\r'))
00990             p--;
00991       }
00992       if (p != message) {
00993          p++;
00994          while (*p == '\n' || *p == '\r')
00995             p++;
00996       }
00997 
00998       buf_size = (buf_size - 1) - (p - message);
00999 
01000       memmove(message, p, buf_size);
01001       message[buf_size] = 0;
01002    }
01003 
01004    return CM_SUCCESS;
01005 }
01006 
01007 /**dox***************************************************************/
01008                                                                                                              /** @} *//* end of msgfunctionc */
01009 
01010 /**dox***************************************************************/
01011 /** @addtogroup cmfunctionc
01012  *
01013  *  @{  */
01014 
01015 /********************************************************************/
01016 /**
01017 Get time from MIDAS server and set local time.
01018 @param    seconds         Time in seconds
01019 @return CM_SUCCESS
01020 */
01021 INT cm_synchronize(DWORD * seconds)
01022 {
01023    INT sec, status;
01024 
01025    /* if connected to server, get time from there */
01026    if (rpc_is_remote()) {
01027       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
01028 
01029       /* set local time */
01030       if (status == CM_SUCCESS)
01031          ss_settime(sec);
01032    }
01033 
01034    /* return time to caller */
01035    if (seconds != NULL) {
01036       *seconds = ss_time();
01037    }
01038 
01039    return CM_SUCCESS;
01040 }
01041 
01042 /********************************************************************/
01043 /**
01044 Get time from MIDAS server and set local time.
01045 @param    str            return time string
01046 @param    buf_size       Maximum size of str
01047 @return   CM_SUCCESS
01048 */
01049 INT cm_asctime(char *str, INT buf_size)
01050 {
01051    /* if connected to server, get time from there */
01052    if (rpc_is_remote())
01053       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
01054 
01055    /* return local time */
01056    strcpy(str, ss_asctime());
01057 
01058    return CM_SUCCESS;
01059 }
01060 
01061 /********************************************************************/
01062 /**
01063 Get time from ss_time on server.
01064 @param    t string
01065 @return   CM_SUCCESS
01066 */
01067 INT cm_time(DWORD * t)
01068 {
01069    /* if connected to server, get time from there */
01070    if (rpc_is_remote())
01071       return rpc_call(RPC_CM_TIME, t);
01072 
01073    /* return local time */
01074    *t = ss_time();
01075 
01076    return CM_SUCCESS;
01077 }
01078 
01079 /**dox***************************************************************/
01080                                                                                                              /** @} *//* end of cmfunctionc */
01081 
01082 /********************************************************************\
01083 *                                                                    *
01084 *           cm_xxx  -  Common Functions to buffer & database         *
01085 *                                                                    *
01086 \********************************************************************/
01087 
01088 /* Globals */
01089 
01090 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
01091 static HNDLE _hDB = 0;          /* Database handle */
01092 static char _client_name[NAME_LENGTH];
01093 static char _path_name[MAX_STRING_LENGTH];
01094 static INT _call_watchdog = TRUE;
01095 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01096 INT _mutex_alarm, _mutex_elog, _mutex_history, _mutex_msg;
01097 
01098 /**dox***************************************************************/
01099 /** @addtogroup cmfunctionc
01100  *
01101  *  @{  */
01102 
01103 /**
01104 Return version number of current MIDAS library as a string
01105 @return version number
01106 */
01107 char *cm_get_version()
01108 {
01109    return MIDAS_VERSION;
01110 }
01111 
01112 /**
01113 Return svn revision number of current MIDAS library as a string
01114 @return revision number
01115 */
01116 int cm_get_revision()
01117 {
01118    return atoi(svn_revision+6);
01119 }
01120 
01121 /********************************************************************/
01122 /**
01123 Set path to actual experiment. This function gets called
01124 by cm_connect_experiment if the connection is established
01125 to a local experiment (not through the TCP/IP server).
01126 The path is then used for all shared memory routines.
01127 @param  path             Pathname
01128 @return CM_SUCCESS
01129 */
01130 INT cm_set_path(char *path)
01131 {
01132    strcpy(_path_name, path);
01133 
01134    /* check for trailing directory seperator */
01135    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01136       strcat(_path_name, DIR_SEPARATOR_STR);
01137 
01138    return CM_SUCCESS;
01139 }
01140 
01141 /********************************************************************/
01142 /**
01143 Return the path name previously set with cm_set_path.
01144 @param  path             Pathname
01145 @return CM_SUCCESS
01146 */
01147 INT cm_get_path(char *path)
01148 {
01149    strcpy(path, _path_name);
01150 
01151    return CM_SUCCESS;
01152 }
01153 
01154 /**dox***************************************************************/
01155                                                                                                              /** @} *//* end of cmfunctionc */
01156 
01157 /**dox***************************************************************/
01158 /** @addtogroup cmfunctionc
01159  *
01160  *  @{  */
01161 
01162 /**dox***************************************************************/
01163 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01164 
01165 typedef struct {
01166    char name[NAME_LENGTH];
01167    char directory[MAX_STRING_LENGTH];
01168    char user[NAME_LENGTH];
01169 } experiment_table;
01170 
01171 static experiment_table exptab[MAX_EXPERIMENT];
01172 
01173 /**dox***************************************************************/
01174 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01175 
01176 /**
01177 Scan the "exptab" file for MIDAS experiment names and save them
01178 for later use by rpc_server_accept(). The file is first searched
01179 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01180 @return CM_SUCCESS<br>
01181         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01182 */
01183 INT cm_scan_experiments(void)
01184 {
01185    INT i;
01186    FILE *f;
01187    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01188 
01189    for (i = 0; i < MAX_EXPERIMENT; i++)
01190       exptab[i].name[0] = 0;
01191 
01192    /* MIDAS_DIR overrides exptab */
01193    if (getenv("MIDAS_DIR")) {
01194       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01195 
01196       strcpy(exptab[0].name, "Default");
01197       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01198       exptab[0].user[0] = 0;
01199 
01200       return CM_SUCCESS;
01201    }
01202 
01203    /* default directory for different OSes */
01204 #if defined (OS_WINNT)
01205    if (getenv("SystemRoot"))
01206       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01207    else if (getenv("windir"))
01208       strlcpy(str, getenv("windir"), sizeof(str));
01209    else
01210       strcpy(str, "");
01211 
01212    strcpy(alt_str, str);
01213    strcat(str, "\\system32\\exptab");
01214    strcat(alt_str, "\\system\\exptab");
01215 #elif defined (OS_UNIX)
01216    strcpy(str, "/etc/exptab");
01217    strcpy(alt_str, "/exptab");
01218 #else
01219    strcpy(str, "exptab");
01220    strcpy(alt_str, "exptab");
01221 #endif
01222 
01223    /* MIDAS_EXPTAB overrides default directory */
01224    if (getenv("MIDAS_EXPTAB")) {
01225       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01226       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01227    }
01228 
01229    /* read list of available experiments */
01230    f = fopen(str, "r");
01231    if (f == NULL) {
01232       f = fopen(alt_str, "r");
01233       if (f == NULL)
01234          return CM_UNDEF_ENVIRON;
01235    }
01236 
01237    i = 0;
01238    if (f != NULL) {
01239       do {
01240          str[0] = 0;
01241          if (fgets(str, 100, f) == NULL)
01242             break;
01243          if (str[0] && str[0] != '#' && str[0] != ' ' && str[0] != '\t' && 
01244             (strchr(str, ' ') || strchr(str, '\t'))) {
01245             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01246 
01247             /* check for trailing directory separator */
01248             pdir = exptab[i].directory;
01249             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01250                strcat(pdir, DIR_SEPARATOR_STR);
01251 
01252             i++;
01253          }
01254       } while (!feof(f));
01255       fclose(f);
01256    }
01257 
01258    /*
01259       for (j=0 ; j<i ; j++)
01260       {
01261       sprintf(str, "Scanned experiment %s", exptab[j].name);
01262       cm_msg(MINFO, str);
01263       }
01264     */
01265 
01266    return CM_SUCCESS;
01267 }
01268 
01269 /********************************************************************/
01270 /**
01271 Delete client info from database
01272 @param hDB               Database handle
01273 @param pid               PID of entry to delete, zero for this process.
01274 @return CM_SUCCESS
01275 */
01276 INT cm_delete_client_info(HNDLE hDB, INT pid)
01277 {
01278 #ifdef LOCAL_ROUTINES
01279 
01280    /* only do it if local */
01281    if (!rpc_is_remote()) {
01282       INT status;
01283       HNDLE hKey;
01284       char str[256];
01285 
01286       if (!pid)
01287          pid = ss_gettid();
01288 
01289       /* don't delete info from a closed database */
01290       if (_database_entries == 0)
01291          return CM_SUCCESS;
01292 
01293       /* make operation atomic by locking database */
01294       db_lock_database(hDB);
01295 
01296       sprintf(str, "System/Clients/%0d", pid);
01297       status = db_find_key1(hDB, 0, str, &hKey);
01298       if (status != DB_SUCCESS) {
01299          db_unlock_database(hDB);
01300          return status;
01301       }
01302 
01303       /* unlock client entry and delete it without locking DB */
01304       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01305       db_delete_key1(hDB, hKey, 1, TRUE);
01306 
01307       db_unlock_database(hDB);
01308 
01309       /* touch notify key to inform others */
01310       status = 0;
01311       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01312    }
01313 #endif                          /*LOCAL_ROUTINES */
01314 
01315    return CM_SUCCESS;
01316 }
01317 
01318 /********************************************************************/
01319 /**
01320 Check if a client with a /system/client/xxx entry has
01321 a valid entry in the ODB client table. If not, remove
01322 that client from the /system/client tree.
01323 @param   hDB               Handle to online database
01324 @param   hKeyClient        Handle to client key
01325 @return  CM_SUCCESS, CM_NO_CLIENT
01326 */
01327 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01328 {
01329 #ifdef LOCAL_ROUTINES
01330 
01331    KEY key;
01332    DATABASE_HEADER *pheader;
01333    DATABASE_CLIENT *pclient;
01334    INT i, client_pid, status, dead = 0, found = 0;
01335    char name[NAME_LENGTH];
01336 
01337    db_get_key(hDB, hKeyClient, &key);
01338    client_pid = atoi(key.name);
01339 
01340    i = sizeof(name);
01341    db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01342 
01343    db_lock_database(hDB);
01344    if (_database[hDB - 1].attached) {
01345       pheader = _database[hDB - 1].database_header;
01346       pclient = pheader->client;
01347 
01348       /* loop through clients */
01349       for (i = 0; i < pheader->max_client_index; i++, pclient++)
01350          if (pclient->tid == client_pid) {
01351             found = 1;
01352             break;
01353          }
01354 
01355 #ifdef OS_UNIX
01356 #ifdef ESRCH
01357       if (found) { /* check that the client is still running: PID still exists */
01358          /* Only enable this for systems that define ESRCH and hope that they also support kill(pid,0) */
01359          errno = 0;
01360          kill(client_pid, 0);
01361          if (errno == ESRCH) {
01362             cm_msg(MERROR, "cm_check_client",
01363                    "Removing client \'%s\', pid %d, index %d because this pid no longer exists",
01364                    pclient->name, client_pid, i);
01365             dead = 1;
01366          }
01367       }
01368 #endif
01369 #endif
01370 
01371       if (!found || dead) {
01372          /* client not found : delete ODB stucture */
01373          db_unlock_database(hDB);
01374 
01375          status = cm_delete_client_info(hDB, client_pid);
01376          if (status != CM_SUCCESS)
01377             cm_msg(MERROR, "cm_check_client", "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid, status);
01378          else
01379             cm_msg(MINFO, "cm_check_client",
01380                    "Deleted entry \'/System/Clients/%d\' for client \'%s\'", client_pid, name);
01381 
01382          return CM_NO_CLIENT;
01383       }
01384    }
01385 
01386    db_unlock_database(hDB);
01387 
01388 #endif                          /*LOCAL_ROUTINES */
01389 
01390    return CM_SUCCESS;
01391 }
01392 
01393 /********************************************************************/
01394 /**
01395 Set client information in online database and return handle
01396 @param  hDB              Handle to online database
01397 @param  hKeyClient       returned key
01398 @param  host_name        server name
01399 @param  client_name      Name of this program as it will be seen
01400                          by other clients.
01401 @param  hw_type          Type of byte order
01402 @param  password         MIDAS password
01403 @param  watchdog_timeout Default watchdog timeout, can be overwritten
01404                          by ODB setting /programs/<name>/Watchdog timeout
01405 @return   CM_SUCCESS
01406 */
01407 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
01408                        char *client_name, INT hw_type, char *password, DWORD watchdog_timeout)
01409 {
01410    if (rpc_is_remote())
01411       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
01412                       host_name, client_name, hw_type, password, watchdog_timeout);
01413 
01414 #ifdef LOCAL_ROUTINES
01415    {
01416       INT status, pid, data, i, idx, size;
01417       HNDLE hKey, hSubkey;
01418       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
01419       BOOL call_watchdog, allow;
01420       PROGRAM_INFO_STR(program_info_str);
01421 
01422       /* check security if password is present */
01423       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
01424       if (hKey) {
01425          /* get password */
01426          size = sizeof(pwd);
01427          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
01428 
01429          /* first check allowed hosts list */
01430          allow = FALSE;
01431          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
01432          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
01433             allow = TRUE;
01434 
01435          /* check allowed programs list */
01436          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
01437          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
01438             allow = TRUE;
01439 
01440          /* now check password */
01441          if (!allow && strcmp(password, pwd) != 0) {
01442             if (password[0])
01443                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
01444             db_close_all_databases();
01445             bm_close_all_buffers();
01446             _msg_buffer = 0;
01447             return CM_WRONG_PASSWORD;
01448          }
01449       }
01450 
01451       /* make following operation atomic by locking database */
01452       db_lock_database(hDB);
01453 
01454       /* check if entry with this pid exists already */
01455       pid = ss_gettid();
01456 
01457       sprintf(str, "System/Clients/%0d", pid);
01458       status = db_find_key(hDB, 0, str, &hKey);
01459       if (status == DB_SUCCESS) {
01460          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
01461          db_delete_key(hDB, hKey, TRUE);
01462       }
01463 
01464       if (strlen(client_name) >= NAME_LENGTH)
01465          client_name[NAME_LENGTH] = 0;
01466 
01467       strcpy(name, client_name);
01468       strcpy(orig_name, client_name);
01469 
01470       /* check if client name already exists */
01471       status = db_find_key(hDB, 0, "System/Clients", &hKey);
01472 
01473       for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
01474          for (i = 0;; i++) {
01475             status = db_enum_key(hDB, hKey, i, &hSubkey);
01476             if (status == DB_NO_MORE_SUBKEYS)
01477                break;
01478 
01479             if (status == DB_SUCCESS) {
01480                size = sizeof(str);
01481                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
01482             }
01483 
01484             /* check if client is living */
01485             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
01486                continue;
01487 
01488             if (equal_ustring(str, name)) {
01489                sprintf(name, "%s%d", client_name, idx);
01490                break;
01491             }
01492          }
01493       }
01494 
01495       /* set name */
01496       sprintf(str, "System/Clients/%0d/Name", pid);
01497       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
01498       if (status != DB_SUCCESS) {
01499          db_unlock_database(hDB);
01500          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
01501          return status;
01502       }
01503 
01504       /* copy new client name */
01505       strcpy(client_name, name);
01506       db_set_client_name(hDB, client_name);
01507 
01508       /* set also as rpc name */
01509       rpc_set_name(client_name);
01510 
01511       /* use /system/clients/PID as root */
01512       sprintf(str, "System/Clients/%0d", pid);
01513       db_find_key(hDB, 0, str, &hKey);
01514 
01515       /* set host name */
01516       status = db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
01517       if (status != DB_SUCCESS) {
01518          db_unlock_database(hDB);
01519          return status;
01520       }
01521 
01522       /* set computer id */
01523       status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT);
01524       if (status != DB_SUCCESS) {
01525          db_unlock_database(hDB);
01526          return status;
01527       }
01528 
01529       /* set server port */
01530       data = 0;
01531       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
01532       if (status != DB_SUCCESS) {
01533          db_unlock_database(hDB);
01534          return status;
01535       }
01536 
01537       /* lock client entry */
01538       db_set_mode(hDB, hKey, MODE_READ, TRUE);
01539 
01540       /* get (set) default watchdog timeout */
01541       size = sizeof(watchdog_timeout);
01542       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
01543       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01544 
01545       /* define /programs entry */
01546       sprintf(str, "/Programs/%s", orig_name);
01547       db_create_record(hDB, 0, str, strcomb(program_info_str));
01548 
01549       /* save handle for ODB and client */
01550       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
01551       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
01552 
01553       /* save watchdog timeout */
01554       cm_get_watchdog_params(&call_watchdog, NULL);
01555       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01556       if (call_watchdog)
01557          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
01558 
01559       /* end of atomic operations */
01560       db_unlock_database(hDB);
01561 
01562       /* touch notify key to inform others */
01563       data = 0;
01564       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
01565 
01566       *hKeyClient = hKey;
01567    }
01568 #endif                          /* LOCAL_ROUTINES */
01569 
01570    return CM_SUCCESS;
01571 }
01572 
01573 /********************************************************************/
01574 /**
01575 Get info about the current client
01576 @param  *client_name       Client name.
01577 @return   CM_SUCCESS, CM_UNDEF_EXP
01578 */
01579 INT cm_get_client_info(char *client_name)
01580 {
01581    INT status, length;
01582    HNDLE hDB, hKey;
01583 
01584    /* get root key of client */
01585    cm_get_experiment_database(&hDB, &hKey);
01586    if (!hDB) {
01587       client_name[0] = 0;
01588       return CM_UNDEF_EXP;
01589    }
01590 
01591    status = db_find_key(hDB, hKey, "Name", &hKey);
01592    if (status != DB_SUCCESS)
01593       return status;
01594 
01595    length = NAME_LENGTH;
01596    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
01597    if (status != DB_SUCCESS)
01598       return status;
01599 
01600    return CM_SUCCESS;
01601 }
01602 
01603 /********************************************************************/
01604 /**
01605 Returns MIDAS environment variables.
01606 @attention This function can be used to evaluate the standard MIDAS
01607            environment variables before connecting to an experiment
01608            (see @ref Environment_variables).
01609            The usual way is that the host name and experiment name are first derived
01610            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
01611            They can then be superseded by command line parameters with -h and -e flags.
01612 \code
01613 #include <stdio.h>
01614 #include <midas.h>
01615 main(int argc, char *argv[])
01616 {
01617   INT  status, i;
01618   char host_name[256],exp_name[32];
01619 
01620   // get default values from environment
01621   cm_get_environment(host_name, exp_name);
01622 
01623   // parse command line parameters
01624   for (i=1 ; i<argc ; i++)
01625     {
01626     if (argv[i][0] == '-')
01627       {
01628       if (i+1 >= argc || argv[i+1][0] == '-')
01629         goto usage;
01630       if (argv[i][1] == 'e')
01631         strcpy(exp_name, argv[++i]);
01632       else if (argv[i][1] == 'h')
01633         strcpy(host_name, argv[++i]);
01634       else
01635         {
01636 usage:
01637         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01638         return 1;
01639         }
01640       }
01641     }
01642   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01643   if (status != CM_SUCCESS)
01644     return 1;
01645     ...do anyting...
01646   cm_disconnect_experiment();
01647 }
01648 \endcode
01649 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
01650 @param host_name_size     string length
01651 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
01652 @param exp_name_size      string length
01653 @return CM_SUCCESS
01654 */
01655 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
01656 {
01657    host_name[0] = exp_name[0] = 0;
01658 
01659    if (getenv("MIDAS_SERVER_HOST"))
01660       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
01661 
01662    if (getenv("MIDAS_EXPT_NAME"))
01663       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
01664 
01665    return CM_SUCCESS;
01666 }
01667 
01668 
01669 /**dox***************************************************************/
01670 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01671 
01672 /********************************************************************/
01673 void cm_check_connect(void)
01674 {
01675    if (_hKeyClient)
01676       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
01677 }
01678 
01679 /**dox***************************************************************/
01680 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01681 
01682 /********************************************************************/
01683 /**
01684 This function connects to an existing MIDAS experiment.
01685 This must be the first call in a MIDAS application.
01686 It opens three TCP connection to the remote host (one for RPC calls,
01687 one to send events and one for hot-link notifications from the remote host)
01688 and writes client information into the ODB under /System/Clients.
01689 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
01690 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
01691 experiment name (see @ref Environment_variables).
01692 For that purpose, the function cm_get_environment()
01693 should be called prior to cm_connect_experiment(). If command line
01694 parameters -h and -e are used, the evaluation should be done between
01695 cm_get_environment() and cm_connect_experiment(). The function
01696 cm_disconnect_experiment() must be called before a MIDAS application exits.
01697 \code
01698 #include <stdio.h>
01699 #include <midas.h>
01700 main(int argc, char *argv[])
01701 {
01702   INT  status, i;
01703   char host_name[256],exp_name[32];
01704 
01705   // get default values from environment
01706   cm_get_environment(host_name, exp_name);
01707 
01708   // parse command line parameters
01709   for (i=1 ; i<argc ; i++)
01710     {
01711     if (argv[i][0] == '-')
01712       {
01713       if (i+1 >= argc || argv[i+1][0] == '-')
01714         goto usage;
01715       if (argv[i][1] == 'e')
01716         strcpy(exp_name, argv[++i]);
01717       else if (argv[i][1] == 'h')
01718         strcpy(host_name, argv[++i]);
01719       else
01720         {
01721 usage:
01722         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
01723         return 1;
01724         }
01725       }
01726     }
01727   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
01728   if (status != CM_SUCCESS)
01729     return 1;
01730   ...do operations...
01731   cm_disconnect_experiment();
01732 }
01733 \endcode
01734 @param host_name Specifies host to connect to. Must be a valid IP host name.
01735   The string can be empty ("") if to connect to the local computer.
01736 @param exp_name Specifies the experiment to connect to.
01737   If this string is empty, the number of defined experiments in exptab is checked.
01738   If only one experiment is defined, the function automatically connects to this
01739   one. If more than one experiment is defined, a list is presented and the user
01740   can interactively select one experiment.
01741 @param client_name Client name of the calling program as it can be seen by
01742   others (like the scl command in ODBEdit).
01743 @param func Callback function to read in a password if security has
01744   been enabled. In all command line applications this function is NULL which
01745   invokes an internal ss_gets() function to read in a password.
01746   In windows environments (MS Windows, X Windows) a function can be supplied to
01747   open a dialog box and read in the password. The argument of this function must
01748   be the returned password.
01749 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br>
01750 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
01751 */
01752 INT cm_connect_experiment(char *host_name, char *exp_name, char *client_name, void (*func) (char *))
01753 {
01754    INT status;
01755    char str[256];
01756 
01757    status = cm_connect_experiment1(host_name, exp_name, client_name,
01758                                    func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
01759    if (status != CM_SUCCESS) {
01760       cm_get_error(status, str);
01761       puts(str);
01762    }
01763 
01764    return status;
01765 }
01766 
01767 /********************************************************************/
01768 /**
01769 Connect to a MIDAS experiment (to the online database) on
01770            a specific host.
01771 @internal
01772 */
01773 INT cm_connect_experiment1(char *host_name, char *exp_name,
01774                            char *client_name, void (*func) (char *), INT odb_size, DWORD watchdog_timeout)
01775 {
01776    INT status, i, mutex_elog, mutex_alarm, mutex_history, mutex_msg, size;
01777    char local_host_name[HOST_NAME_LENGTH];
01778    char client_name1[NAME_LENGTH];
01779    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
01780    HNDLE hDB, hKeyClient;
01781    BOOL call_watchdog;
01782    RUNINFO_STR(runinfo_str);
01783 
01784    if (_hKeyClient)
01785       cm_disconnect_experiment();
01786 
01787    rpc_set_name(client_name);
01788 
01789    /* check for local host */
01790    if (equal_ustring(host_name, "local"))
01791       host_name[0] = 0;
01792 
01793 #ifdef OS_WINNT
01794    {
01795       WSADATA WSAData;
01796 
01797       /* Start windows sockets */
01798       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
01799          return RPC_NET_ERROR;
01800    }
01801 #endif
01802 
01803    /* search for experiment name in exptab */
01804    if (exp_name == NULL)
01805       exp_name = "";
01806 
01807    strcpy(exp_name1, exp_name);
01808    if (exp_name1[0] == 0) {
01809       status = cm_select_experiment(host_name, exp_name1);
01810       if (status != CM_SUCCESS)
01811          return status;
01812    }
01813 
01814    /* connect to MIDAS server */
01815    if (host_name[0]) {
01816       status = rpc_server_connect(host_name, exp_name1);
01817       if (status != RPC_SUCCESS)
01818          return status;
01819 
01820       /* register MIDAS library functions */
01821       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
01822       if (status != RPC_SUCCESS)
01823          return status;
01824    } else {
01825       /* lookup path for *SHM files and save it */
01826       status = cm_scan_experiments();
01827       if (status != CM_SUCCESS)
01828          return status;
01829 
01830       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
01831          if (equal_ustring(exp_name1, exptab[i].name))
01832             break;
01833 
01834       /* return if experiment not defined */
01835       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
01836          /* message should be displayed by application
01837             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
01838             cm_msg(MERROR, str);
01839           */
01840          return CM_UNDEF_EXP;
01841       }
01842 
01843       cm_set_path(exptab[i].directory);
01844 
01845       /* create alarm and elog mutexes */
01846       status = ss_mutex_create("ALARM", &mutex_alarm);
01847       if (status != SS_CREATED && status != SS_SUCCESS) {
01848          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
01849          return status;
01850       }
01851       status = ss_mutex_create("ELOG", &mutex_elog);
01852       if (status != SS_CREATED && status != SS_SUCCESS) {
01853          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
01854          return status;
01855       }
01856       status = ss_mutex_create("HISTORY", &mutex_history);
01857       if (status != SS_CREATED && status != SS_SUCCESS) {
01858          cm_msg(MERROR, "cm_connect_experiment", "Cannot create history mutex");
01859          return status;
01860       }
01861       status = ss_mutex_create("MSG", &mutex_msg);
01862       if (status != SS_CREATED && status != SS_SUCCESS) {
01863          cm_msg(MERROR, "cm_connect_experiment", "Cannot create message mutex");
01864          return status;
01865       }
01866 
01867       cm_set_experiment_mutex(mutex_alarm, mutex_elog, mutex_history, mutex_msg);
01868    }
01869 
01870    /* open ODB */
01871    if (odb_size == 0)
01872       odb_size = DEFAULT_ODB_SIZE;
01873 
01874    status = db_open_database("ODB", odb_size, &hDB, client_name);
01875    if (status != DB_SUCCESS && status != DB_CREATED) {
01876       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01877       return status;
01878    }
01879 
01880    /* now setup client info */
01881    gethostname(local_host_name, sizeof(local_host_name));
01882 
01883    /* check watchdog timeout */
01884    if (watchdog_timeout == 0)
01885       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01886 
01887    strcpy(client_name1, client_name);
01888    password[0] = 0;
01889    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01890                                client_name1, rpc_get_option(0, RPC_OHW_TYPE), password, watchdog_timeout);
01891 
01892    if (status == CM_WRONG_PASSWORD) {
01893       if (func == NULL)
01894          strcpy(str, ss_getpass("Password: "));
01895       else
01896          func(str);
01897 
01898       /* re-open database */
01899       status = db_open_database("ODB", odb_size, &hDB, client_name);
01900       if (status != DB_SUCCESS && status != DB_CREATED) {
01901          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
01902          return status;
01903       }
01904 
01905       strcpy(password, ss_crypt(str, "mi"));
01906       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
01907                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE), password, watchdog_timeout);
01908       if (status != CM_SUCCESS) {
01909          /* disconnect */
01910          if (rpc_is_remote())
01911             rpc_server_disconnect();
01912 
01913          return status;
01914       }
01915    }
01916 
01917    cm_set_experiment_database(hDB, hKeyClient);
01918 
01919    /* set experiment name in ODB */
01920    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
01921 
01922    /* set data dir in ODB */
01923    cm_get_path(str);
01924    size = sizeof(str);
01925    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
01926 
01927    /* check /runinfo structure */
01928    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), TRUE);
01929    if (status == DB_STRUCT_MISMATCH) {
01930       cm_msg(MERROR, "cm_connect_experiment1", "Aborting on mismatching /Runinfo structure");
01931       cm_disconnect_experiment();
01932       abort();
01933    }
01934 
01935    /* register server to be able to be called by other clients */
01936    status = cm_register_server();
01937    if (status != CM_SUCCESS)
01938       return status;
01939 
01940    /* set watchdog timeout */
01941    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
01942    size = sizeof(watchdog_timeout);
01943    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
01944    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
01945    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
01946 
01947    /* send startup notification */
01948    if (strchr(local_host_name, '.'))
01949       *strchr(local_host_name, '.') = 0;
01950 
01951    /* startup message is not displayed */
01952    _message_print = NULL;
01953 
01954    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started", client_name, local_host_name);
01955 
01956    /* enable system and user messages to stdout as default */
01957    cm_set_msg_print(MT_ALL, MT_ALL, puts);
01958 
01959    /* call cm_check_connect when exiting */
01960    atexit((void (*)(void)) cm_check_connect);
01961 
01962    /* register ctrl-c handler */
01963    ss_ctrlc_handler(cm_ctrlc_handler);
01964 
01965    return CM_SUCCESS;
01966 }
01967 
01968 /********************************************************************/
01969 /**
01970 Connect to a MIDAS server and return all defined
01971            experiments in *exp_name[MAX_EXPERIMENTS]
01972 @param  host_name         Internet host name.
01973 @param  exp_name          list of experiment names
01974 @return CM_SUCCESS, RPC_NET_ERROR
01975 */
01976 INT cm_list_experiments(char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
01977 {
01978    INT i, status;
01979    struct sockaddr_in bind_addr;
01980    INT sock;
01981    char str[MAX_EXPERIMENT * NAME_LENGTH];
01982    struct hostent *phe;
01983 
01984    if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
01985       status = cm_scan_experiments();
01986       if (status != CM_SUCCESS)
01987          return status;
01988 
01989       for (i = 0; i < MAX_EXPERIMENT; i++)
01990          strcpy(exp_name[i], exptab[i].name);
01991 
01992       return CM_SUCCESS;
01993    }
01994 #ifdef OS_WINNT
01995    {
01996       WSADATA WSAData;
01997 
01998       /* Start windows sockets */
01999       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02000          return RPC_NET_ERROR;
02001    }
02002 #endif
02003 
02004    /* create a new socket for connecting to remote server */
02005    sock = socket(AF_INET, SOCK_STREAM, 0);
02006    if (sock == -1) {
02007       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
02008       return RPC_NET_ERROR;
02009    }
02010 
02011    /* connect to remote node */
02012    memset(&bind_addr, 0, sizeof(bind_addr));
02013    bind_addr.sin_family = AF_INET;
02014    bind_addr.sin_addr.s_addr = 0;
02015    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
02016 
02017 #ifdef OS_VXWORKS
02018    {
02019       INT host_addr;
02020 
02021       host_addr = hostGetByName(host_name);
02022       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
02023    }
02024 #else
02025    phe = gethostbyname(host_name);
02026    if (phe == NULL) {
02027       cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
02028       return RPC_NET_ERROR;
02029    }
02030    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
02031 #endif
02032 
02033 #ifdef OS_UNIX
02034    do {
02035       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
02036 
02037       /* don't return if an alarm signal was cought */
02038    } while (status == -1 && errno == EINTR);
02039 #else
02040    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
02041 #endif
02042 
02043    if (status != 0) {
02044 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
02045       return RPC_NET_ERROR;
02046    }
02047 
02048    /* request experiment list */
02049    send(sock, "I", 2, 0);
02050 
02051    for (i = 0; i < MAX_EXPERIMENT; i++) {
02052       exp_name[i][0] = 0;
02053       status = recv_string(sock, str, sizeof(str), 1000);
02054 
02055       if (status < 0)
02056          return RPC_NET_ERROR;
02057 
02058       if (status == 0)
02059          break;
02060 
02061       strcpy(exp_name[i], str);
02062    }
02063 
02064    exp_name[i][0] = 0;
02065    closesocket(sock);
02066 
02067    return CM_SUCCESS;
02068 }
02069 
02070 /********************************************************************/
02071 /**
02072 Connect to a MIDAS server and select an experiment
02073            from the experiments available on this server
02074 @internal
02075 @param  host_name         Internet host name.
02076 @param  exp_name          list of experiment names
02077 @return CM_SUCCESS, RPC_NET_ERROR
02078 */
02079 INT cm_select_experiment(char *host_name, char *exp_name)
02080 {
02081    INT status, i;
02082    char expts[MAX_EXPERIMENT][NAME_LENGTH];
02083    char str[32];
02084 
02085    /* retrieve list of experiments and make selection */
02086    status = cm_list_experiments(host_name, expts);
02087    if (status != CM_SUCCESS)
02088       return status;
02089 
02090    if (expts[1][0]) {
02091       if (host_name[0])
02092          printf("Available experiments on server %s:\n", host_name);
02093       else
02094          printf("Available experiments on local computer:\n");
02095 
02096       for (i = 0; expts[i][0]; i++)
02097          printf("%d : %s\n", i, expts[i]);
02098       printf("Select number: ");
02099       ss_gets(str, 32);
02100       i = atoi(str);
02101       strcpy(exp_name, expts[i]);
02102    } else
02103       strcpy(exp_name, expts[0]);
02104 
02105    return CM_SUCCESS;
02106 }
02107 
02108 /********************************************************************/
02109 /**
02110 Connect to a MIDAS client of the current experiment
02111 @internal
02112 @param  client_name       Name of client to connect to. This name
02113                             is set by the other client via the
02114                             cm_connect_experiment call.
02115 @param  hConn            Connection handle
02116 @return CM_SUCCESS, CM_NO_CLIENT
02117 */
02118 INT cm_connect_client(char *client_name, HNDLE * hConn)
02119 {
02120    HNDLE hDB, hKeyRoot, hSubkey, hKey;
02121    INT status, i, length, port;
02122    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
02123 
02124    /* find client entry in ODB */
02125    cm_get_experiment_database(&hDB, &hKey);
02126 
02127    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02128    if (status != DB_SUCCESS)
02129       return status;
02130 
02131    i = 0;
02132    do {
02133       /* search for client with specific name */
02134       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02135       if (status == DB_NO_MORE_SUBKEYS)
02136          return CM_NO_CLIENT;
02137 
02138       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02139       if (status != DB_SUCCESS)
02140          return status;
02141 
02142       length = NAME_LENGTH;
02143       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02144       if (status != DB_SUCCESS)
02145          return status;
02146 
02147       if (equal_ustring(name, client_name)) {
02148          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02149          if (status != DB_SUCCESS)
02150             return status;
02151 
02152          length = sizeof(INT);
02153          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02154          if (status != DB_SUCCESS)
02155             return status;
02156 
02157          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02158          if (status != DB_SUCCESS)
02159             return status;
02160 
02161          length = sizeof(host_name);
02162          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02163          if (status != DB_SUCCESS)
02164             return status;
02165 
02166          /* client found -> connect to its server port */
02167          return rpc_client_connect(host_name, port, client_name, hConn);
02168       }
02169 
02170 
02171    } while (TRUE);
02172 }
02173 
02174 /********************************************************************/
02175 /**
02176 Disconnect from a MIDAS client
02177 @param   hConn             Connection handle obtained via
02178                              cm_connect_client()
02179 @param   bShutdown         If TRUE, disconnect from client and
02180                              shut it down (exit the client program)
02181                              by sending a RPC_SHUTDOWN message
02182 @return   see rpc_client_disconnect()
02183 */
02184 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02185 {
02186    return rpc_client_disconnect(hConn, bShutdown);
02187 }
02188 
02189 /********************************************************************/
02190 /**
02191 Disconnect from a MIDAS experiment.
02192 @attention Should be the last call to a MIDAS library function in an
02193 application before it exits. This function removes the client information
02194 from the ODB, disconnects all TCP connections and frees all internal
02195 allocated memory. See cm_connect_experiment() for example.
02196 @return CM_SUCCESS
02197 */
02198 INT cm_disconnect_experiment(void)
02199 {
02200    HNDLE hDB, hKey;
02201    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02202 
02203    /* send shutdown notification */
02204    rpc_get_name(client_name);
02205    gethostname(local_host_name, sizeof(local_host_name));
02206    if (strchr(local_host_name, '.'))
02207       *strchr(local_host_name, '.') = 0;
02208 
02209    /* disconnect message not displayed */
02210    _message_print = NULL;
02211 
02212    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name, local_host_name);
02213 
02214    if (rpc_is_remote()) {
02215       /* close open records */
02216       db_close_all_records();
02217 
02218       rpc_client_disconnect(-1, FALSE);
02219       rpc_server_disconnect();
02220    } else {
02221       rpc_client_disconnect(-1, FALSE);
02222 
02223 #ifdef LOCAL_ROUTINES
02224       ss_alarm(0, cm_watchdog);
02225       _watchdog_last_called = 0;
02226 #endif                          /* LOCAL_ROUTINES */
02227 
02228       /* delete client info */
02229       cm_get_experiment_database(&hDB, &hKey);
02230 
02231       if (hDB)
02232          cm_delete_client_info(hDB, 0);
02233 
02234       bm_close_all_buffers();
02235       db_close_all_databases();
02236    }
02237 
02238    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02239       rpc_server_shutdown();
02240 
02241    /* free RPC list */
02242    rpc_deregister_functions();
02243 
02244    cm_set_experiment_database(0, 0);
02245 
02246    _msg_buffer = 0;
02247 
02248    /* free memory buffers */
02249    if (_event_buffer_size > 0) {
02250       M_FREE(_event_buffer);
02251       _event_buffer_size = 0;
02252    }
02253 
02254    if (_net_recv_buffer_size > 0) {
02255       M_FREE(_net_recv_buffer);
02256       _net_recv_buffer_size = 0;
02257    }
02258 
02259    if (_net_send_buffer_size > 0) {
02260       M_FREE(_net_send_buffer);
02261       _net_send_buffer_size = 0;
02262    }
02263 
02264    if (_tcp_buffer != NULL) {
02265       M_FREE(_tcp_buffer);
02266       _tcp_buffer = NULL;
02267    }
02268 
02269    return CM_SUCCESS;
02270 }
02271 
02272 /********************************************************************/
02273 /**
02274 Set the handle to the ODB for the currently connected experiment
02275 @param hDB              Database handle
02276 @param hKeyClient       Key handle of client structure
02277 @return CM_SUCCESS
02278 */
02279 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02280 {
02281    _hDB = hDB;
02282    _hKeyClient = hKeyClient;
02283 
02284    return CM_SUCCESS;
02285 }
02286 
02287 
02288 
02289 /**dox***************************************************************/
02290 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02291 
02292 /********************************************************************/
02293 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog, INT mutex_history, INT mutex_msg)
02294 /********************************************************************\
02295 
02296   Routine: cm_set_experiment_mutex
02297 
02298   Purpose: Set the handle to the experiment wide mutexes
02299 
02300   Input:
02301     INT    mutex_alarm      Alarm mutex
02302     INT    mutex_elog       Elog mutex
02303     INT    mutex_history    History mutex
02304     INT    mutex_msg        Message mutex
02305 
02306   Output:
02307     none
02308 
02309   Function value:
02310     CM_SUCCESS              Successful completion
02311 
02312 \********************************************************************/
02313 {
02314    _mutex_alarm = mutex_alarm;
02315    _mutex_elog = mutex_elog;
02316    _mutex_history = mutex_history;
02317    _mutex_msg = mutex_msg;
02318 
02319    return CM_SUCCESS;
02320 }
02321 
02322 /**dox***************************************************************/
02323 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02324 
02325 /********************************************************************/
02326 /**
02327 Get the handle to the ODB from the currently connected experiment.
02328 
02329 @attention This function returns the handle of the online database (ODB) which
02330 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02331 to access the client information in the ODB. If the client key handle is not needed,
02332 the parameter can be NULL.
02333 \code
02334 HNDLE hDB, hkeyclient;
02335  char  name[32];
02336  int   size;
02337  db_get_experiment_database(&hdb, &hkeyclient);
02338  size = sizeof(name);
02339  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02340  printf("My name is %s\n", name);
02341 \endcode
02342 @param hDB Database handle.
02343 @param hKeyClient Handle for key where search starts, zero for root.
02344 @return CM_SUCCESS
02345 */
02346 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02347 {
02348    if (_hDB) {
02349       if (hDB != NULL)
02350          *hDB = _hDB;
02351       if (hKeyClient != NULL)
02352          *hKeyClient = _hKeyClient;
02353    } else {
02354       if (hDB != NULL)
02355          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02356       if (hKeyClient != NULL)
02357          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02358    }
02359 
02360    return CM_SUCCESS;
02361 }
02362 
02363 /**dox***************************************************************/
02364 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02365 
02366 /********************************************************************/
02367 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog, INT *mutex_history, INT *mutex_msg)
02368 /********************************************************************\
02369 
02370   Routine: cm_get_experiment_mutex
02371 
02372   Purpose: Get the handle to the experiment wide mutexes
02373 
02374   Input:
02375     none
02376 
02377   Output:
02378     INT    mutex_alarm      Alarm mutex
02379     INT    mutex_elog       Elog mutex
02380     INT    mutex_history    History mutex
02381     INT    mutex_msg        Message mutex
02382 
02383   Function value:
02384     CM_SUCCESS              Successful completion
02385 
02386 \********************************************************************/
02387 {
02388    if (mutex_alarm)
02389       *mutex_alarm = _mutex_alarm;
02390    if (mutex_elog)
02391       *mutex_elog = _mutex_elog;
02392    if (mutex_history)
02393       *mutex_history = _mutex_history;
02394    if (mutex_msg)
02395       *mutex_msg = _mutex_msg;
02396 
02397    return CM_SUCCESS;
02398 }
02399 
02400 /**dox***************************************************************/
02401 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02402 
02403 static int bm_validate_client_index(const BUFFER * buf)
02404 {
02405    int badindex = 0;
02406    BUFFER_CLIENT *bcl = buf->buffer_header->client;
02407 
02408    if (buf->client_index < 0)
02409       badindex = 1;
02410    else if (buf->client_index > buf->buffer_header->max_client_index)
02411       badindex = 1;
02412    else {
02413       bcl = &(buf->buffer_header->client[buf->client_index]);
02414       if (bcl->name[0] == 0)
02415          badindex = 1;
02416       else if (bcl->pid != ss_getpid())
02417          badindex = 1;
02418    }
02419 
02420 #if 0
02421    printf("bm_validate_client_index: badindex=%d, buf=%p, client_index=%d, max_client_index=%d, client_name=\'%s\', client_pid=%d, pid=%d\n",
02422           badindex,
02423           buf,
02424           buf->client_index,
02425           buf->buffer_header->max_client_index,
02426           buf->buffer_header->client[buf->client_index].name,
02427           buf->buffer_header->client[buf->client_index].pid, ss_getpid());
02428 #endif
02429 
02430    if (badindex) {
02431       static int prevent_recursion = 1;
02432       if (prevent_recursion) {
02433          prevent_recursion = 0;
02434          cm_msg(MERROR, "bm_validate_client_index",
02435                 "Invalid client index %d in buffer \'%s\'. Client name \'%s\', pid %d should be %d",
02436                 buf->client_index, buf->buffer_header->name, bcl->name, bcl->pid, ss_getpid());
02437          cm_msg(MERROR, "bm_validate_client_index",
02438                 "Maybe this client was removed by a timeout. Cannot continue, exiting.");
02439       }
02440       exit(1);
02441    }
02442 
02443    return buf->client_index;
02444 }
02445 
02446 /********************************************************************/
02447 /**
02448 Sets the internal watchdog flags and the own timeout.
02449 If call_watchdog is TRUE, the cm_watchdog routine is called
02450 periodically from the system to show other clients that
02451 this application is "alive". On UNIX systems, the
02452 alarm() timer is used which is then not available for
02453 user purposes.
02454 
02455 The timeout specifies the time, after which the calling
02456 application should be considered "dead" by other clients.
02457 Normally, the cm_watchdog() routines is called periodically.
02458 If a client crashes, this does not occur any more. Then
02459 other clients can detect this and clear all buffer and
02460 database entries of this application so they are not
02461 blocked any more. If this application should not checked
02462 by others, the timeout can be specified as zero.
02463 It might be useful for debugging purposes to do so,
02464 because if a debugger comes to a breakpoint and stops
02465 the application, the periodic call of cm_watchdog
02466 is disabled and the client looks like dead.
02467 
02468 If the timeout is not zero, but the watchdog is not
02469 called (call_watchdog == FALSE), the user must ensure
02470 to call cm_watchdog periodically with a period of
02471 WATCHDOG_INTERVAL milliseconds or less.
02472 
02473 An application which calles system routines which block
02474 the alarm signal for some time, might increase the
02475 timeout to the maximum expected blocking time before
02476 issuing the calls. One example is the logger doing
02477 Exabyte tape IO, which can take up to one minute.
02478 @param    call_watchdog   Call the cm_watchdog routine periodically
02479 @param    timeout         Timeout for this application in ms
02480 @return   CM_SUCCESS
02481 */
02482 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
02483 {
02484    INT i;
02485 
02486    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
02487    _watchdog_timeout = timeout;
02488 
02489    if (rpc_is_remote())
02490       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
02491 
02492 #ifdef LOCAL_ROUTINES
02493 
02494    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
02495       HNDLE hDB, hKey;
02496 
02497       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
02498 
02499       /* write timeout value to client enty in ODB */
02500       cm_get_experiment_database(&hDB, &hKey);
02501 
02502       if (hDB) {
02503          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02504          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
02505          db_set_mode(hDB, hKey, MODE_READ, TRUE);
02506       }
02507    } else {
02508       _call_watchdog = call_watchdog;
02509       _watchdog_timeout = timeout;
02510 
02511       /* set watchdog flag of all open buffers */
02512       for (i = _buffer_entries; i > 0; i--) {
02513          BUFFER_CLIENT *pclient;
02514          BUFFER_HEADER *pheader;
02515          INT idx;
02516 
02517          idx = bm_validate_client_index(&_buffer[i - 1]);
02518          pheader = _buffer[i - 1].buffer_header;
02519          pclient = &pheader->client[idx];
02520 
02521          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02522              _buffer[i - 1].index != rpc_get_server_acception())
02523             continue;
02524 
02525          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i - 1].index != ss_gettid())
02526             continue;
02527 
02528          if (!_buffer[i - 1].attached)
02529             continue;
02530 
02531          /* clear entry from client structure in buffer header */
02532          pclient->watchdog_timeout = timeout;
02533 
02534          /* show activity */
02535          pclient->last_activity = ss_millitime();
02536       }
02537 
02538       /* set watchdog flag of alll open databases */
02539       for (i = _database_entries; i > 0; i--) {
02540          DATABASE_HEADER *pheader;
02541          DATABASE_CLIENT *pclient;
02542          INT idx;
02543 
02544          db_lock_database(i);
02545          idx = _database[i - 1].client_index;
02546          pheader = _database[i - 1].database_header;
02547          pclient = &pheader->client[idx];
02548 
02549          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
02550              _database[i - 1].index != rpc_get_server_acception()) {
02551             db_unlock_database(i);
02552             continue;
02553          }
02554 
02555          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _database[i - 1].index != ss_gettid()) {
02556             db_unlock_database(i);
02557             continue;
02558          }
02559 
02560          if (!_database[i - 1].attached) {
02561             db_unlock_database(i);
02562             continue;
02563          }
02564 
02565          /* clear entry from client structure in buffer header */
02566          pclient->watchdog_timeout = timeout;
02567 
02568          /* show activity */
02569          pclient->last_activity = ss_millitime();
02570 
02571          db_unlock_database(i);
02572       }
02573 
02574       if (call_watchdog)
02575          /* restart watchdog */
02576          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02577       else
02578          /* kill current timer */
02579          ss_alarm(0, cm_watchdog);
02580    }
02581 
02582 #endif                          /* LOCAL_ROUTINES */
02583 
02584    return CM_SUCCESS;
02585 }
02586 
02587 /********************************************************************/
02588 /**
02589 Return the current watchdog parameters
02590 @param call_watchdog   Call the cm_watchdog routine periodically
02591 @param timeout         Timeout for this application in seconds
02592 @return   CM_SUCCESS
02593 */
02594 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
02595 {
02596    if (call_watchdog)
02597       *call_watchdog = _call_watchdog;
02598    if (timeout)
02599       *timeout = _watchdog_timeout;
02600 
02601    return CM_SUCCESS;
02602 }
02603 
02604 /********************************************************************/
02605 /**
02606 Return watchdog information about specific client
02607 @param    hDB              ODB handle
02608 @param    client_name     ODB client name
02609 @param    timeout         Timeout for this application in seconds
02610 @param    last            Last time watchdog was called in msec
02611 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE
02612 */
02613 
02614 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
02615 {
02616    if (rpc_is_remote())
02617       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
02618 
02619 #ifdef LOCAL_ROUTINES
02620    {
02621       DATABASE_HEADER *pheader;
02622       DATABASE_CLIENT *pclient;
02623       INT i;
02624 
02625       if (hDB > _database_entries || hDB <= 0) {
02626          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02627          return DB_INVALID_HANDLE;
02628       }
02629 
02630       if (!_database[hDB - 1].attached) {
02631          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
02632          return DB_INVALID_HANDLE;
02633       }
02634 
02635       /* lock database */
02636       db_lock_database(hDB);
02637 
02638       pheader = _database[hDB - 1].database_header;
02639       pclient = pheader->client;
02640 
02641       /* find client */
02642       for (i = 0; i < pheader->max_client_index; i++, pclient++)
02643          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
02644             *timeout = pclient->watchdog_timeout;
02645             *last = ss_millitime() - pclient->last_activity;
02646             db_unlock_database(hDB);
02647             return CM_SUCCESS;
02648          }
02649 
02650       *timeout = *last = 0;
02651 
02652       db_unlock_database(hDB);
02653 
02654       return CM_NO_CLIENT;
02655    }
02656 #else                           /* LOCAL_ROUTINES */
02657    return CM_SUCCESS;
02658 #endif                          /* LOCAL_ROUTINES */
02659 }
02660 
02661 
02662 /**dox***************************************************************/
02663 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02664 
02665 /********************************************************************/
02666 INT cm_register_server(void)
02667 /********************************************************************\
02668 
02669   Routine: cm_register_server
02670 
02671   Purpose: Register a server which can be called from other clients
02672            of a specific experiment.
02673 
02674   Input:
02675     none
02676 
02677   Output:
02678     none
02679 
02680   Function value:
02681     CM_SUCCESS              Successful completion
02682 
02683 \********************************************************************/
02684 {
02685    INT status, port;
02686    HNDLE hDB, hKey;
02687 
02688    if (!_server_registered) {
02689       port = 0;
02690       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
02691       if (status != RPC_SUCCESS)
02692          return status;
02693       _server_registered = TRUE;
02694 
02695       /* register MIDAS library functions */
02696       rpc_register_functions(rpc_get_internal_list(1), NULL);
02697 
02698       /* store port number in ODB */
02699       cm_get_experiment_database(&hDB, &hKey);
02700 
02701       status = db_find_key(hDB, hKey, "Server Port", &hKey);
02702       if (status != DB_SUCCESS)
02703          return status;
02704 
02705       /* unlock database */
02706       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02707 
02708       /* set value */
02709       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
02710       if (status != DB_SUCCESS)
02711          return status;
02712 
02713       /* lock database */
02714       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02715    }
02716 
02717    return CM_SUCCESS;
02718 }
02719 
02720 /**dox***************************************************************/
02721 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02722 
02723 /********************************************************************/
02724 /**
02725 Registers a callback function for run transitions.
02726 This function internally registers the transition callback
02727 function and publishes its request for transition notification by writing
02728 a transition request to /System/Clients/<pid>/Transition XXX.
02729 Other clients making a transition scan the transition requests of all clients
02730 and call their transition callbacks via RPC.
02731 
02732 Clients can register for transitions (Start/Stop/Pause/Resume) in a given
02733 sequence. All sequence numbers given in the registration are sorted on
02734 a transition and the clients are contacted in ascending order. By default,
02735 all programs register with a sequence number of 500. The logger however
02736 uses 200 for start, so that it can open files before the other clients
02737 are contacted, and 800 for stop, so that the files get closed when all
02738 other clients have gone already through the stop trantition.
02739 
02740 The callback function returns CM_SUCCESS if it can perform the transition or
02741 a value larger than one in case of error. An error string can be copied
02742 into the error variable.
02743 @attention The callback function will be called on transitions from inside the
02744     cm_yield() function which therefore must be contained in the main program loop.
02745 \code
02746 INT start(INT run_number, char *error)
02747 {
02748   if (<not ok>)
02749     {
02750     strcpy(error, "Cannot start because ...");
02751     return 2;
02752     }
02753   printf("Starting run %d\n", run_number);
02754   return CM_SUCCESS;
02755 }
02756 main()
02757 {
02758   ...
02759   cm_register_transition(TR_START, start, 500);
02760   do
02761     {
02762     status = cm_yield(1000);
02763     } while (status != RPC_SHUTDOWN &&
02764              status != SS_ABORT);
02765   ...
02766 }
02767 \endcode
02768 @param transition Transition to register for (see @ref state_transition)
02769 @param func Callback function.
02770 @param sequence_number Sequence number for that transition (1..1000)
02771 @return CM_SUCCESS
02772 */
02773 INT cm_register_transition(INT transition, INT(*func) (INT, char *), INT sequence_number)
02774 {
02775    INT status, i;
02776    HNDLE hDB, hKey, hKeyTrans;
02777    KEY key;
02778    char str[256];
02779 
02780    /* check for valid transition */
02781    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
02782       cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
02783       return CM_INVALID_TRANSITION;
02784    }
02785 
02786    cm_get_experiment_database(&hDB, &hKey);
02787 
02788    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
02789 
02790    /* register new transition request */
02791 
02792    /* find empty slot */
02793    for (i = 0; i < MAX_TRANSITIONS; i++)
02794       if (!_trans_table[i].transition)
02795          break;
02796 
02797    if (i == MAX_TRANSITIONS) {
02798       cm_msg(MERROR, "cm_register_transition",
02799              "To many transition registrations. Please increase MAX_TRANSITIONS and recompile");
02800       return CM_TOO_MANY_REQUESTS;
02801    }
02802 
02803    _trans_table[i].transition = transition;
02804    _trans_table[i].func = func;
02805    _trans_table[i].sequence_number = sequence_number;
02806 
02807    for (i = 0; ; i++)
02808       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
02809          break;
02810 
02811    sprintf(str, "Transition %s", trans_name[i].name);
02812 
02813    /* unlock database */
02814    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02815 
02816    /* set value */
02817    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02818    if (!hKeyTrans) {
02819       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02820       if (status != DB_SUCCESS)
02821          return status;
02822    } else {
02823       status = db_get_key(hDB, hKeyTrans, &key);
02824       if (status != DB_SUCCESS)
02825          return status;
02826       status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT);
02827       if (status != DB_SUCCESS)
02828          return status;
02829    }
02830 
02831    /* re-lock database */
02832    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02833 
02834    return CM_SUCCESS;
02835 }
02836 
02837 INT cm_deregister_transition(INT transition)
02838 {
02839    INT status, i;
02840    HNDLE hDB, hKey, hKeyTrans;
02841    char str[256];
02842 
02843    /* check for valid transition */
02844    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
02845       cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
02846       return CM_INVALID_TRANSITION;
02847    }
02848 
02849    cm_get_experiment_database(&hDB, &hKey);
02850 
02851    /* remove existing transition request */
02852    for (i = 0; i < MAX_TRANSITIONS; i++)
02853       if (_trans_table[i].transition == transition)
02854          break;
02855 
02856    if (i == MAX_TRANSITIONS) {
02857       cm_msg(MERROR, "cm_register_transition",
02858              "Cannot de-register transition registration, request not found");
02859       return CM_INVALID_TRANSITION;
02860    }
02861 
02862    _trans_table[i].transition = 0;
02863    _trans_table[i].func = NULL;
02864    _trans_table[i].sequence_number = 0;
02865 
02866    for (i = 0; ; i++)
02867       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
02868          break;
02869 
02870    sprintf(str, "Transition %s", trans_name[i].name);
02871 
02872    /* unlock database */
02873    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02874 
02875    /* set value */
02876    status = db_find_key(hDB, hKey, str, &hKeyTrans);
02877    if (hKeyTrans) {
02878       status = db_delete_key(hDB, hKeyTrans, FALSE);
02879       if (status != DB_SUCCESS)
02880          return status;
02881    }
02882 
02883    /* re-lock database */
02884    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02885 
02886    return CM_SUCCESS;
02887 }
02888 
02889 /********************************************************************/
02890 /**
02891 Change the transition sequence for the calling program.
02892 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
02893 @param sequence_number New sequence number, should be between 1 and 1000
02894 @return     CM_SUCCESS
02895 */
02896 INT cm_set_transition_sequence(INT transition, INT sequence_number)
02897 {
02898    INT status, i;
02899    HNDLE hDB, hKey;
02900    char str[256];
02901 
02902    /* check for valid transition */
02903    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
02904       cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
02905       return CM_INVALID_TRANSITION;
02906    }
02907 
02908    cm_get_experiment_database(&hDB, &hKey);
02909 
02910    /* Find the transition type from the list */
02911    for (i = 0; ; i++)
02912       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
02913          break;
02914    sprintf(str, "Transition %s", trans_name[i].name);
02915 
02916    /* Change local sequence number for this transition type */
02917    for (i = 0; i < MAX_TRANSITIONS; i++)
02918       if (_trans_table[i].transition == transition) {
02919          _trans_table[i].sequence_number = sequence_number;
02920          break;
02921       }
02922 
02923    /* unlock database */
02924    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02925 
02926    /* set value */
02927    status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
02928    if (status != DB_SUCCESS)
02929       return status;
02930 
02931    /* re-lock database */
02932    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02933 
02934    return CM_SUCCESS;
02935 
02936 }
02937 
02938 /**dox***************************************************************/
02939 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02940 
02941 static INT _requested_transition;
02942 static DWORD _deferred_transition_mask;
02943 
02944 /**dox***************************************************************/
02945 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02946 
02947 /********************************************************************/
02948 /**
02949 Register a deferred transition handler. If a client is
02950 registered as a deferred transition handler, it may defer
02951 a requested transition by returning FALSE until a certain
02952 condition (like a motor reaches its end position) is
02953 reached.
02954 @param transition      One of TR_xxx
02955 @param (*func)         Function which gets called whenever
02956                        a transition is requested. If it returns
02957                        FALSE, the transition is not performed.
02958 @return CM_SUCCESS,    <error> Error from ODB access
02959 */
02960 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
02961 {
02962    INT status, i, size;
02963    char tr_key_name[256];
02964    HNDLE hDB, hKey;
02965 
02966    cm_get_experiment_database(&hDB, &hKey);
02967 
02968    for (i = 0; _deferred_trans_table[i].transition; i++)
02969       if (_deferred_trans_table[i].transition == transition)
02970          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
02971 
02972    /* set new transition mask */
02973    _deferred_transition_mask |= transition;
02974 
02975    for (i = 0; ; i++)
02976       if (trans_name[i].name[0] == 0 || trans_name[i].transition == transition)
02977          break;
02978 
02979    sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
02980 
02981    /* unlock database */
02982    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02983 
02984    /* set value */
02985    i = 0;
02986    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT);
02987    if (status != DB_SUCCESS)
02988       return status;
02989 
02990    /* re-lock database */
02991    db_set_mode(hDB, hKey, MODE_READ, TRUE);
02992 
02993    /* hot link requested transition */
02994    size = sizeof(_requested_transition);
02995    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT, TRUE);
02996    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
02997    status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
02998    if (status != DB_SUCCESS) {
02999       cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
03000       return status;
03001    }
03002 
03003    return CM_SUCCESS;
03004 }
03005 
03006 /********************************************************************/
03007 /**
03008 Check for any deferred transition. If a deferred transition
03009 handler has been registered via the
03010 cm_register_deferred_transition function, this routine
03011 should be called regularly. It checks if a transition
03012 request is pending. If so, it calld the registered handler
03013 if the transition should be done and then actually does
03014 the transition.
03015 @return     CM_SUCCESS, <error>  Error from cm_transition()
03016 */
03017 INT cm_check_deferred_transition()
03018 {
03019    INT i, status;
03020    char str[256];
03021    static BOOL first;
03022 
03023    if (_requested_transition == 0)
03024       first = TRUE;
03025 
03026    if (_requested_transition & _deferred_transition_mask) {
03027       for (i = 0; _deferred_trans_table[i].transition; i++)
03028          if (_deferred_trans_table[i].transition == _requested_transition)
03029             break;
03030 
03031       if (_deferred_trans_table[i].transition == _requested_transition) {
03032          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition, first)) {
03033             status = cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str), SYNC, FALSE);
03034             if (status != CM_SUCCESS)
03035                cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
03036 
03037             /* bypass hotlink and set _requested_transition directly to zero */
03038             _requested_transition = 0;
03039 
03040             return status;
03041          }
03042          first = FALSE;
03043       }
03044    }
03045 
03046    return SUCCESS;
03047 }
03048 
03049 
03050 /**dox***************************************************************/
03051 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03052 
03053 /********************************************************************/
03054 
03055 /**dox***************************************************************/
03056 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03057 
03058 typedef struct {
03059    int sequence_number;
03060    char host_name[HOST_NAME_LENGTH];
03061    char client_name[NAME_LENGTH];
03062    int port;
03063 } TR_CLIENT;
03064 
03065 int tr_compare(const void *arg1, const void *arg2)
03066 {
03067    return ((TR_CLIENT *) arg1)->sequence_number - ((TR_CLIENT *) arg2)->sequence_number;
03068 }
03069 
03070 /********************************************************************/
03071 /**
03072 Performs a run transition (Start/Stop/Pause/Resume).
03073 
03074 Synchronous/Asynchronous flag.
03075 If set to ASYNC, the transition is done
03076 asynchronously, meaning that clients are connected and told to execute their
03077 callback routine, but no result is awaited. The return value is
03078 specified by the transition callback function on the remote clients. If all callbacks
03079 can perform the transition, CM_SUCCESS is returned. If one callback cannot
03080 perform the transition, the return value of this callback is returned from
03081 cm_transition().
03082 The async_flag is usually FALSE so that transition callbacks can block a
03083 run transition in case of problems and return an error string. The only exception are
03084 situations where a run transition is performed automatically by a program which
03085 cannot block in a transition. For example the logger can cause a run stop when a
03086 disk is nearly full but it cannot block in the cm_transition() function since it
03087 has its own run stop callback which must flush buffers and close disk files and
03088 tapes.
03089 \code
03090 ...
03091     i = 1;
03092     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03093 
03094       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
03095       if (status != CM_SUCCESS)
03096       {
03097         // in case of error
03098         printf("Error: %s\n", str);
03099       }
03100     ...
03101 \endcode
03102 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03103 @param run_number New run number. If zero, use current run number plus one.
03104 @param errstr returned error string.
03105 @param errstr_size Size of error string.
03106 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
03107 @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
03108 @return CM_SUCCESS, <error> error code from remote client
03109 */
03110 INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
03111 {
03112    INT i, j, status, idx, size, sequence_number, port, state, old_timeout, n_tr_clients;
03113    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hConn, hKeyTrans;
03114    DWORD seconds;
03115    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH], str[256], error[256], tr_key_name[256];
03116    char *trname = "unknown";
03117    KEY key;
03118    BOOL deferred;
03119    PROGRAM_INFO program_info;
03120    TR_CLIENT *tr_client;
03121 
03122    deferred = (transition & TR_DEFERRED) > 0;
03123    transition &= ~TR_DEFERRED;
03124 
03125    /* check for valid transition */
03126    if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
03127       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03128       if (errstr != NULL)
03129          strlcpy(errstr, "Invalid transition request", errstr_size);
03130       return CM_INVALID_TRANSITION;
03131    }
03132 
03133    /* get key of local client */
03134    cm_get_experiment_database(&hDB, &hKeylocal);
03135 
03136    if (errstr != NULL)
03137      strlcpy(errstr, "Unknown error", errstr_size);
03138 
03139    /* if no run number is given, get it from DB */
03140    if (run_number == 0) {
03141       size = sizeof(run_number);
03142       status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
03143       assert(status == SUCCESS);
03144    }
03145 
03146    if (run_number <= 0) {
03147       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
03148       abort();
03149    }
03150 
03151    /* check if transition in progress */
03152    i = 0;
03153    size = sizeof(i);
03154    db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT, TRUE);
03155    if (i == 1) {
03156       strlcpy(errstr, "Start/Stop already in progress, please try again later\n", errstr_size);
03157       strlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
03158       return CM_TRANSITION_IN_PROGRESS;
03159    }
03160 
03161    /* indicate transition in progress */
03162    i = 1;
03163    db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03164 
03165    /* clear run abort flag */
03166    i = 0;
03167    db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03168 
03169    /* Set new run number in ODB */
03170    if (transition == TR_START) {
03171       if (debug_flag == 1)
03172          printf("Setting run number %d in ODB\n", run_number);
03173       if (debug_flag == 2)
03174          cm_msg(MDEBUG, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
03175 
03176       status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT);
03177       assert(status == SUCCESS);
03178       if (status != DB_SUCCESS)
03179          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
03180    }
03181 
03182    if (deferred) {
03183       /* remove transition request */
03184       i = 0;
03185       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
03186    } else {
03187       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03188       if (status != DB_SUCCESS) {
03189          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03190          if (errstr != NULL)
03191             strlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
03192          return status;
03193       }
03194 
03195       /* check if deferred transition already in progress */
03196       size = sizeof(i);
03197       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
03198       if (i) {
03199          if (errstr != NULL)
03200             strlcpy(errstr, "Deferred transition already in progress", errstr_size);
03201          return CM_TRANSITION_IN_PROGRESS;
03202       }
03203 
03204       for (i = 0; trans_name[i].name[0] != 0; i++)
03205          if (trans_name[i].transition == transition) {
03206             trname = trans_name[i].name;
03207             break;
03208          }
03209 
03210       sprintf(tr_key_name, "Transition %s DEFERRED", trname);
03211 
03212       /* search database for clients with deferred transition request */
03213       for (i = 0, status = 0;; i++) {
03214          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03215          if (status == DB_NO_MORE_SUBKEYS)
03216             break;
03217 
03218          if (status == DB_SUCCESS) {
03219             size = sizeof(sequence_number);
03220             status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT, FALSE);
03221 
03222             /* if registered for deferred transition, set flag in ODB and return */
03223             if (status == DB_SUCCESS) {
03224                size = NAME_LENGTH;
03225                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03226                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT);
03227 
03228                if (debug_flag == 1)
03229                   printf("---- Transition %s deferred by client \"%s\" ----\n", trname, str);
03230                if (debug_flag == 2)
03231                   cm_msg(MDEBUG, "cm_transition",
03232                          "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname, str);
03233 
03234                if (errstr)
03235                   sprintf(errstr, "Transition %s deferred by client \"%s\"", trname, str);
03236 
03237                return CM_DEFERRED_TRANSITION;
03238             }
03239          }
03240       }
03241    }
03242 
03243    /* execute programs on start */
03244    if (transition == TR_START) {
03245       str[0] = 0;
03246       size = sizeof(str);
03247       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
03248       if (str[0])
03249          ss_system(str);
03250 
03251       db_find_key(hDB, 0, "/Programs", &hRootKey);
03252       if (hRootKey) {
03253          for (i = 0;; i++) {
03254             status = db_enum_key(hDB, hRootKey, i, &hKey);
03255             if (status == DB_NO_MORE_SUBKEYS)
03256                break;
03257 
03258             db_get_key(hDB, hKey, &key);
03259 
03260             /* don't check "execute on xxx" */
03261             if (key.type != TID_KEY)
03262                continue;
03263 
03264             size = sizeof(program_info);
03265             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03266             if (status != DB_SUCCESS) {
03267                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03268                continue;
03269             }
03270 
03271             if (program_info.auto_start && program_info.start_command[0])
03272                ss_system(program_info.start_command);
03273          }
03274       }
03275    }
03276 
03277    /* set new start time in database */
03278    if (transition == TR_START) {
03279       /* ASCII format */
03280       cm_asctime(str, sizeof(str));
03281       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03282 
03283       /* reset stop time */
03284       seconds = 0;
03285       db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03286 
03287       /* Seconds since 1.1.1970 */
03288       cm_time(&seconds);
03289       db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03290    }
03291 
03292    /* set stop time in database */
03293    if (transition == TR_STOP) {
03294       size = sizeof(state);
03295       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03296       if (status != DB_SUCCESS)
03297          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03298 
03299       if (state != STATE_STOPPED) {
03300          /* stop time binary */
03301          cm_time(&seconds);
03302          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_DWORD);
03303          if (status != DB_SUCCESS)
03304             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
03305 
03306          /* stop time ascii */
03307          cm_asctime(str, sizeof(str));
03308          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03309          if (status != DB_SUCCESS)
03310             cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
03311       }
03312    }
03313 
03314    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03315    if (status != DB_SUCCESS) {
03316       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03317       if (errstr)
03318          strlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
03319       return status;
03320    }
03321 
03322    for (i = 0; trans_name[i].name[0] != 0; i++)
03323       if (trans_name[i].transition == transition) {
03324          trname = trans_name[i].name;
03325          break;
03326       }
03327 
03328    /* check that all transition clients are alive */
03329    for (i=0;; ) {
03330       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03331       if (status != DB_SUCCESS)
03332          break;
03333 
03334       status = cm_check_client(hDB, hSubkey);
03335 
03336       if (status == DB_SUCCESS) {
03337          /* this client is alive. Check next one! */
03338          i++;
03339          continue;
03340       }
03341 
03342       assert(status == CM_NO_CLIENT);
03343 
03344       /* start from scratch: removing odb entries as we iterate over them
03345        * does strange things to db_enum_key() */
03346       i=0;
03347    }
03348 
03349    if (debug_flag == 1)
03350       printf("---- Transition %s started ----\n", trname);
03351    if (debug_flag == 2)
03352       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s started ----", trname);
03353 
03354    sprintf(tr_key_name, "Transition %s", trname);
03355 
03356    /* search database for clients which registered for transition */
03357    n_tr_clients = 0;
03358    tr_client = NULL;
03359 
03360    for (i = 0, status = 0;; i++) {
03361       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03362       if (status == DB_NO_MORE_SUBKEYS)
03363          break;
03364 
03365       if (status == DB_SUCCESS) {
03366          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
03367 
03368          if (status == DB_SUCCESS) {
03369 
03370             db_get_key(hDB, hKeyTrans, &key);
03371 
03372             for (j = 0; j < key.num_values; j++) {
03373                size = sizeof(sequence_number);
03374                status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT);
03375                assert(status == DB_SUCCESS);
03376 
03377                if (tr_client == NULL)
03378                   tr_client = (TR_CLIENT *) malloc(sizeof(TR_CLIENT));
03379                else
03380                   tr_client = (TR_CLIENT *) realloc(tr_client, sizeof(TR_CLIENT) * (n_tr_clients + 1));
03381                assert(tr_client);
03382 
03383                tr_client[n_tr_clients].sequence_number = sequence_number;
03384 
03385                if (hSubkey == hKeylocal) {
03386                   /* remember own client */
03387                   tr_client[n_tr_clients].port = 0;
03388                } else {
03389                   /* get client info */
03390                   size = sizeof(client_name);
03391                   db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
03392                   strcpy(tr_client[n_tr_clients].client_name, client_name);
03393 
03394                   size = sizeof(port);
03395                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03396                   tr_client[n_tr_clients].port = port;
03397 
03398                   size = sizeof(host_name);
03399                   db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03400                   strcpy(tr_client[n_tr_clients].host_name, host_name);
03401                }
03402 
03403                n_tr_clients++;
03404             }
03405          }
03406       }
03407    }
03408 
03409    /* sort clients according to sequence number */
03410    if (n_tr_clients > 1)
03411       qsort(tr_client, n_tr_clients, sizeof(TR_CLIENT), tr_compare);
03412 
03413    /* contact ordered clients for transition */
03414    for (idx = 0; idx < n_tr_clients; idx++) {
03415       /* erase error string */
03416       error[0] = 0;
03417 
03418       if (debug_flag == 1)
03419          printf("\n==== Found client \"%s\" with sequence number %d\n",
03420                 tr_client[idx].client_name, tr_client[idx].sequence_number);
03421       if (debug_flag == 2)
03422          cm_msg(MDEBUG, "cm_transition",
03423                 "cm_transition: ==== Found client \"%s\" with sequence number %d",
03424                 tr_client[idx].client_name, tr_client[idx].sequence_number);
03425 
03426       /* if own client call transition callback directly */
03427       if (tr_client[idx].port == 0) {
03428          for (i = 0; _trans_table[i].transition; i++)
03429             if (_trans_table[i].transition == transition)
03430                break;
03431 
03432          /* call registered function */
03433          if (_trans_table[i].transition == transition && _trans_table[i].func) {
03434             if (debug_flag == 1)
03435                printf("Calling local transition callback\n");
03436             if (debug_flag == 2)
03437                cm_msg(MDEBUG, "cm_transition", "cm_transition: Calling local transition callback");
03438 
03439             status = _trans_table[i].func(run_number, error);
03440 
03441             if (debug_flag == 1)
03442                printf("Local transition callback finished\n");
03443             if (debug_flag == 2)
03444                cm_msg(MDEBUG, "cm_transition", "cm_transition: Local transition callback finished");
03445          } else
03446             status = CM_SUCCESS;
03447 
03448          if (errstr != NULL)
03449             memcpy(errstr, error, (INT) strlen(error) + 1 < errstr_size ? (INT) strlen(error) + 1 : errstr_size);
03450 
03451          if (status != CM_SUCCESS) {
03452             /* indicate abort */
03453             i = 1;
03454             db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03455             i = 0;
03456             db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03457 
03458             free(tr_client);
03459             return status;
03460          }
03461 
03462       } else {
03463 
03464          /* contact client if transition mask set */
03465          if (debug_flag == 1)
03466             printf("Connecting to client \"%s\" on host %s...\n",
03467                    tr_client[idx].client_name, tr_client[idx].host_name);
03468          if (debug_flag == 2)
03469             cm_msg(MDEBUG, "cm_transition",
03470                    "cm_transition: Connecting to client \"%s\" on host %s...",
03471                    tr_client[idx].client_name, tr_client[idx].host_name);
03472 
03473          /* client found -> connect to its server port */
03474          status = rpc_client_connect(tr_client[idx].host_name, tr_client[idx].port,
03475                                      tr_client[idx].client_name, &hConn);
03476          if (status != RPC_SUCCESS) {
03477             cm_msg(MERROR, "cm_transition",
03478                    "cannot connect to client \"%s\" on host %s, port %d, status %d",
03479                    tr_client[idx].client_name, tr_client[idx].host_name, tr_client[idx].port, status);
03480             if (errstr != NULL)
03481                strlcpy(errstr, "Cannot connect to client", errstr_size);
03482 
03483             /* indicate abort */
03484             i = 1;
03485             db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03486             i = 0;
03487             db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03488 
03489             return status;
03490          }
03491 
03492          if (debug_flag == 1)
03493             printf("Connection established to client \"%s\" on host %s\n",
03494                    tr_client[idx].client_name, tr_client[idx].host_name);
03495          if (debug_flag == 2)
03496             cm_msg(MDEBUG, "cm_transition",
03497                    "cm_transition: Connection established to client \"%s\" on host %s",
03498                    tr_client[idx].client_name, tr_client[idx].host_name);
03499 
03500          /* call RC_TRANSITION on remote client with increased timeout */
03501          old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03502          rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
03503 
03504          /* set FTPC protocol if in async mode */
03505          if (async_flag == ASYNC)
03506             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03507 
03508          if (debug_flag == 1)
03509             printf("Executing RPC transition client \"%s\" on host %s...\n",
03510                    tr_client[idx].client_name, tr_client[idx].host_name);
03511          if (debug_flag == 2)
03512             cm_msg(MDEBUG, "cm_transition",
03513                    "cm_transition: Executing RPC transition client \"%s\" on host %s...",
03514                    tr_client[idx].client_name, tr_client[idx].host_name);
03515 
03516          status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
03517                                   run_number, error, errstr_size, tr_client[idx].sequence_number);
03518 
03519          /* reset timeout */
03520          rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03521 
03522          /* reset protocol */
03523          if (async_flag == ASYNC)
03524             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
03525 
03526          if (debug_flag == 1)
03527             printf("RPC transition finished client \"%s\" on host %s with status %d\n",
03528                    tr_client[idx].client_name, tr_client[idx].host_name, status);
03529          if (debug_flag == 2)
03530             cm_msg(MDEBUG, "cm_transition",
03531                    "cm_transition: RPC transition finished client \"%s\" on host %s with status %d",
03532                    tr_client[idx].client_name, tr_client[idx].host_name, status);
03533 
03534          if (errstr != NULL) {
03535             if (strlen(error) < 2)
03536                sprintf(errstr, "Unknown error %d from client \'%s\' on host %s", status, tr_client[idx].client_name, tr_client[idx].host_name);
03537             else
03538                memcpy(errstr, error, (INT) strlen(error) + 1 < (INT) errstr_size ? (INT) strlen(error) + 1 : errstr_size);
03539          }
03540 
03541          if (status != CM_SUCCESS) {
03542             /* indicate abort */
03543             i = 1;
03544             db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT);
03545             i = 0;
03546             db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03547 
03548             free(tr_client);
03549             return status;
03550          }
03551       }
03552    }
03553 
03554    if (tr_client)
03555       free(tr_client);
03556 
03557    if (debug_flag == 1)
03558       printf("\n---- Transition %s finished ----\n", trname);
03559    if (debug_flag == 2)
03560       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname);
03561 
03562    /* set new run state in database */
03563    if (transition == TR_START || transition == TR_RESUME)
03564       state = STATE_RUNNING;
03565 
03566    if (transition == TR_PAUSE)
03567       state = STATE_PAUSED;
03568 
03569    if (transition == TR_STOP)
03570       state = STATE_STOPPED;
03571 
03572    size = sizeof(state);
03573    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
03574    if (status != DB_SUCCESS)
03575       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
03576 
03577    /* send notification message */
03578    str[0] = 0;
03579    if (transition == TR_START)
03580       sprintf(str, "Run #%d started", run_number);
03581    if (transition == TR_STOP)
03582       sprintf(str, "Run #%d stopped", run_number);
03583    if (transition == TR_PAUSE)
03584       sprintf(str, "Run #%d paused", run_number);
03585    if (transition == TR_RESUME)
03586       sprintf(str, "Run #%d resumed", run_number);
03587 
03588    if (str[0])
03589       cm_msg(MINFO, "cm_transition", str);
03590 
03591    /* lock/unlock ODB values if present */
03592    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
03593    if (hKey && transition == TR_START)
03594       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03595    if (hKey && transition == TR_STOP)
03596       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
03597 
03598    /* flush online database */
03599    if (transition == TR_STOP)
03600       db_flush_database(hDB);
03601 
03602    /* execute/stop programs on stop */
03603    if (transition == TR_STOP) {
03604       str[0] = 0;
03605       size = sizeof(str);
03606       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
03607       if (str[0])
03608          ss_system(str);
03609 
03610       db_find_key(hDB, 0, "/Programs", &hRootKey);
03611       if (hRootKey) {
03612          for (i = 0;; i++) {
03613             status = db_enum_key(hDB, hRootKey, i, &hKey);
03614             if (status == DB_NO_MORE_SUBKEYS)
03615                break;
03616 
03617             db_get_key(hDB, hKey, &key);
03618 
03619             /* don't check "execute on xxx" */
03620             if (key.type != TID_KEY)
03621                continue;
03622 
03623             size = sizeof(program_info);
03624             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03625             if (status != DB_SUCCESS) {
03626                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03627                continue;
03628             }
03629 
03630             if (program_info.auto_stop)
03631                cm_shutdown(key.name, FALSE);
03632          }
03633       }
03634    }
03635 
03636 
03637    /* indicate success */
03638    i = 0;
03639    db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03640 
03641    if (errstr != NULL)
03642       strlcpy(errstr, "Success", errstr_size);
03643 
03644    return CM_SUCCESS;
03645 }
03646 
03647 
03648 
03649 /**dox***************************************************************/
03650 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03651 
03652 /********************************************************************/
03653 INT cm_dispatch_ipc(char *message, int s)
03654 /********************************************************************\
03655 
03656   Routine: cm_dispatch_ipc
03657 
03658   Purpose: Called from ss_suspend if an IPC message arrives
03659 
03660   Input:
03661     INT   msg               IPC message we got, MSG_ODB/MSG_BM
03662     INT   p1, p2            Optional parameters
03663     int   s                 Optional server socket
03664 
03665   Output:
03666     none
03667 
03668   Function value:
03669     CM_SUCCESS              Successful completion
03670 
03671 \********************************************************************/
03672 {
03673    if (message[0] == 'O') {
03674       HNDLE hDB, hKey;
03675       sscanf(message + 2, "%d %d", &hDB, &hKey);
03676       return db_update_record(hDB, hKey, s);
03677    }
03678 
03679    /* message == "B  " means "resume event sender" */
03680    if (message[0] == 'B' && message[2] != ' ') {
03681       char str[80];
03682 
03683       strcpy(str, message + 2);
03684       if (strchr(str, ' '))
03685          *strchr(str, ' ') = 0;
03686 
03687       if (s)
03688          return bm_notify_client(str, s);
03689       else
03690          return bm_push_event(str);
03691    }
03692 
03693    return CM_SUCCESS;
03694 }
03695 
03696 /********************************************************************/
03697 static BOOL _ctrlc_pressed = FALSE;
03698 
03699 void cm_ctrlc_handler(int sig)
03700 {
03701    int i;
03702 
03703    i = sig; /* avoid compiler warning */
03704 
03705    if (_ctrlc_pressed) {
03706       printf("Received 2nd break. Hard abort.\n");
03707       exit(0);
03708    }
03709    printf("Received break. Aborting...\n");
03710    _ctrlc_pressed = TRUE;
03711 
03712    ss_ctrlc_handler(cm_ctrlc_handler);
03713 }
03714 
03715 BOOL cm_is_ctrlc_pressed()
03716 {
03717    return _ctrlc_pressed;
03718 }
03719 
03720 void cm_ack_ctrlc_pressed()
03721 {
03722    _ctrlc_pressed = FALSE;
03723 }
03724 
03725 
03726 /**dox***************************************************************/
03727 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03728 
03729 /********************************************************************/
03730 /**
03731 Central yield functions for clients. This routine should
03732 be called in an infinite loop by a client in order to
03733 give the MIDAS system the opportunity to receive commands
03734 over RPC channels, update database records and receive
03735 events.
03736 @param millisec         Timeout in millisec. If no message is
03737                         received during the specified timeout,
03738                         the routine returns. If millisec=-1,
03739                         it only returns when receiving an
03740                         RPC_SHUTDOWN message.
03741 @return CM_SUCCESS, RPC_SHUTDOWN
03742 */
03743 INT cm_yield(INT millisec)
03744 {
03745    INT status;
03746    BOOL bMore;
03747    static DWORD last_checked = 0;
03748 
03749    /* check for ctrl-c */
03750    if (_ctrlc_pressed)
03751       return RPC_SHUTDOWN;
03752 
03753    /* check for available events */
03754    if (rpc_is_remote()) {
03755       bMore = bm_poll_event(TRUE);
03756       if (bMore)
03757          status = ss_suspend(0, 0);
03758       else
03759          status = ss_suspend(millisec, 0);
03760 
03761       return status;
03762    }
03763 
03764    /* check alarms once every 10 seconds */
03765    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
03766       al_check();
03767       last_checked = ss_time();
03768    }
03769 
03770    bMore = bm_check_buffers();
03771 
03772    if (bMore) {
03773       /* if events available, quickly check other IPC channels */
03774       status = ss_suspend(0, 0);
03775    } else {
03776       /* mark event buffers for ready-to-receive */
03777       bm_mark_read_waiting(TRUE);
03778 
03779       status = ss_suspend(millisec, 0);
03780 
03781       /* unmark event buffers for ready-to-receive */
03782       bm_mark_read_waiting(FALSE);
03783    }
03784 
03785    return status;
03786 }
03787 
03788 /********************************************************************/
03789 /**
03790 Executes command via system() call
03791 @param    command          Command string to execute
03792 @param    result           stdout of command
03793 @param    bufsize          string size in byte
03794 @return   CM_SUCCESS
03795 */
03796 INT cm_execute(char *command, char *result, INT bufsize)
03797 {
03798    char str[256];
03799    INT n;
03800    int fh;
03801 
03802    if (rpc_is_remote())
03803       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
03804 
03805    if (bufsize > 0) {
03806       strcpy(str, command);
03807       sprintf(str, "%s > %d.tmp", command, ss_getpid());
03808 
03809       system(str);
03810 
03811       sprintf(str, "%d.tmp", ss_getpid());
03812       fh = open(str, O_RDONLY, 0644);
03813       result[0] = 0;
03814       if (fh) {
03815          n = read(fh, result, bufsize - 1);
03816          result[MAX(0, n)] = 0;
03817          close(fh);
03818       }
03819       remove(str);
03820    } else
03821       system(command);
03822 
03823    return CM_SUCCESS;
03824 }
03825 
03826 
03827 
03828 /**dox***************************************************************/
03829 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03830 
03831 /********************************************************************/
03832 INT cm_register_function(INT id, INT(*func) (INT, void **))
03833 /********************************************************************\
03834 
03835   Routine: cm_register_function
03836 
03837   Purpose: Call rpc_register_function and publish the registered
03838            function under system/clients/<pid>/RPC
03839 
03840   Input:
03841     INT      id             RPC ID
03842     INT      *func          New dispatch function
03843 
03844   Output:
03845    <implicit: func gets copied to rpc_list>
03846 
03847   Function value:
03848    CM_SUCCESS               Successful completion
03849    RPC_INVALID_ID           RPC ID not found
03850 
03851 \********************************************************************/
03852 {
03853    HNDLE hDB, hKey;
03854    INT status;
03855    char str[80];
03856 
03857    status = rpc_register_function(id, func);
03858    if (status != RPC_SUCCESS)
03859       return status;
03860 
03861    cm_get_experiment_database(&hDB, &hKey);
03862 
03863    /* create new key for this id */
03864    status = 1;
03865    sprintf(str, "RPC/%d", id);
03866 
03867    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03868    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
03869    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03870 
03871    if (status != DB_SUCCESS)
03872       return status;
03873 
03874    return CM_SUCCESS;
03875 }
03876 
03877 
03878 /**dox***************************************************************/
03879 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03880 
03881 /**dox***************************************************************/
03882                                                                                                              /** @} *//* end of cmfunctionc */
03883 
03884 /**dox***************************************************************/
03885 /** @addtogroup bmfunctionc
03886  *
03887  *  @{  */
03888 
03889 /********************************************************************\
03890 *                                                                    *
03891 *                 bm_xxx  -  Buffer Manager Functions                *
03892 *                                                                    *
03893 \********************************************************************/
03894 
03895 /********************************************************************/
03896 /**
03897 Check if an event matches a given event request by the
03898 event id and trigger mask
03899 @param event_id      Event ID of request
03900 @param trigger_mask  Trigger mask of request
03901 @param pevent    Pointer to event to check
03902 @return TRUE      if event matches request
03903 */
03904 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
03905 {
03906    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 || (pevent->event_id & 0xF000) == EVENTID_FRAG)
03907       /* fragmented event */
03908       return ((event_id == EVENTID_ALL ||
03909                event_id == (pevent->event_id & 0x0FFF)) &&
03910               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03911 
03912    return ((event_id == EVENTID_ALL ||
03913             event_id == pevent->event_id) &&
03914            (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
03915 }
03916 
03917 /********************************************************************/
03918 /**
03919 Open an event buffer.
03920 Two default buffers are created by the system.
03921 The "SYSTEM" buffer is used to
03922 exchange events and the "SYSMSG" buffer is used to exchange system messages.
03923 The name and size of the event buffers is defined in midas.h as
03924 EVENT_BUFFER_NAME and 2*MAX_EVENT_SIZE.
03925 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
03926 enters a main loop. Events are then received in process_event()
03927 \code
03928 #include <stdio.h>
03929 #include "midas.h"
03930 void process_event(HNDLE hbuf, HNDLE request_id,
03931            EVENT_HEADER *pheader, void *pevent)
03932 {
03933   printf("Received event #%d\r",
03934   pheader->serial_number);
03935 }
03936 main()
03937 {
03938   INT status, request_id;
03939   HNDLE hbuf;
03940   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
03941   if (status != CM_SUCCESS)
03942   return 1;
03943   bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
03944   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
03945 
03946   do
03947   {
03948    status = cm_yield(1000);
03949   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
03950   cm_disconnect_experiment();
03951   return 0;
03952 }
03953 \endcode
03954 @param buffer_name Name of buffer
03955 @param buffer_size Default size of buffer in bytes. Can by overwritten with ODB value
03956 @param buffer_handle Buffer handle returned by function
03957 @return BM_SUCCESS, BM_CREATED <br>
03958 BM_NO_SHM Shared memory cannot be created <br>
03959 BM_NO_MUTEX Mutex cannot be created <br>
03960 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
03961 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
03962 different size <br>
03963 BM_INVALID_PARAM Invalid parameter
03964 */
03965 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
03966 {
03967    INT status;
03968 
03969    if (rpc_is_remote()) {
03970       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
03971       bm_mark_read_waiting(TRUE);
03972       return status;
03973    }
03974 #ifdef LOCAL_ROUTINES
03975    {
03976       INT i, handle, size;
03977       BUFFER_CLIENT *pclient;
03978       BOOL shm_created;
03979       HNDLE shm_handle;
03980       BUFFER_HEADER *pheader;
03981       HNDLE hDB, odb_key;
03982       char odb_path[256];
03983 
03984       /* get buffer size from ODB, user parameter as default if not present in ODB */
03985       sprintf(odb_path, "/Experiment/Buffer sizes/%s", buffer_name);
03986       status = cm_get_experiment_database(&hDB, &odb_key);
03987       assert(status == SUCCESS);
03988       size = sizeof(INT);
03989       status = db_get_value(hDB, 0, odb_path, &buffer_size, &size, TID_DWORD, TRUE);
03990 
03991       if (buffer_size <= 0 || buffer_size > 1 * 1024 * 1024 * 1024) {
03992          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size %d", buffer_size);
03993          return BM_INVALID_PARAM;
03994       }
03995 
03996       if (!buffer_name[0]) {
03997          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
03998          return BM_INVALID_PARAM;
03999       }
04000 
04001       /* allocate new space for the new buffer descriptor */
04002       if (_buffer_entries == 0) {
04003          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
04004          memset(_buffer, 0, sizeof(BUFFER));
04005          if (_buffer == NULL) {
04006             *buffer_handle = 0;
04007             return BM_NO_MEMORY;
04008          }
04009 
04010          _buffer_entries = 1;
04011          i = 0;
04012       } else {
04013          /* check if buffer alreay is open */
04014          for (i = 0; i < _buffer_entries; i++)
04015             if (_buffer[i].attached && equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
04016                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04017                    _buffer[i].index != rpc_get_server_acception())
04018                   continue;
04019 
04020                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i].index != ss_gettid())
04021                   continue;
04022 
04023                *buffer_handle = i + 1;
04024                return BM_SUCCESS;
04025             }
04026 
04027          /* check for a deleted entry */
04028          for (i = 0; i < _buffer_entries; i++)
04029             if (!_buffer[i].attached)
04030                break;
04031 
04032          /* if not found, create new one */
04033          if (i == _buffer_entries) {
04034             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
04035             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
04036 
04037             _buffer_entries++;
04038             if (_buffer == NULL) {
04039                _buffer_entries--;
04040                *buffer_handle = 0;
04041                return BM_NO_MEMORY;
04042             }
04043          }
04044       }
04045 
04046       handle = i;
04047 
04048       if (strlen(buffer_name) >= NAME_LENGTH)
04049          buffer_name[NAME_LENGTH] = 0;
04050 
04051       /* reduce buffer size is larger than maximum */
04052 #ifdef MAX_SHM_SIZE
04053       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
04054          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
04055 #endif
04056 
04057       /* open shared memory region */
04058       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04059                            (void **) &(_buffer[handle].buffer_header), &shm_handle, FALSE);
04060 
04061       if (status != SS_SUCCESS && status != SS_CREATED) {
04062          *buffer_handle = 0;
04063          _buffer_entries--;
04064          return BM_NO_SHM;
04065       }
04066 
04067       pheader = _buffer[handle].buffer_header;
04068 
04069       shm_created = (status == SS_CREATED);
04070 
04071       if (shm_created) {
04072          /* setup header info if buffer was created */
04073          memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
04074 
04075          strcpy(pheader->name, buffer_name);
04076          pheader->size = buffer_size;
04077       } else {
04078          /* check if buffer size is identical */
04079          if (pheader->size != buffer_size) {
04080             cm_msg(MERROR, "bm_open_buffer", "Requested buffer size (%d) differs from existing size (%d)",
04081                    buffer_size, pheader->size);
04082             *buffer_handle = 0;
04083             _buffer_entries--;
04084             return BM_MEMSIZE_MISMATCH;
04085          }
04086       }
04087 
04088       /* create mutex for the buffer */
04089       status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
04090       if (status != SS_CREATED && status != SS_SUCCESS) {
04091          *buffer_handle = 0;
04092          _buffer_entries--;
04093          return BM_NO_MUTEX;
04094       }
04095 
04096       /* first lock buffer */
04097       bm_lock_buffer(handle + 1);
04098 
04099       /*
04100          Now we have a BUFFER_HEADER, so let's setup a CLIENT
04101          structure in that buffer. The information there can also
04102          be seen by other processes.
04103        */
04104 
04105       for (i = 0; i < MAX_CLIENTS; i++)
04106          if (pheader->client[i].pid == 0)
04107             break;
04108 
04109       if (i == MAX_CLIENTS) {
04110          bm_unlock_buffer(handle + 1);
04111          *buffer_handle = 0;
04112          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
04113          return BM_NO_SLOT;
04114       }
04115 
04116       /* store slot index in _buffer structure */
04117       _buffer[handle].client_index = i;
04118 
04119       /*
04120          Save the index of the last client of that buffer so that later only
04121          the clients 0..max_client_index-1 have to be searched through.
04122        */
04123       pheader->num_clients++;
04124       if (i + 1 > pheader->max_client_index)
04125          pheader->max_client_index = i + 1;
04126 
04127       /* setup buffer header and client structure */
04128       pclient = &pheader->client[i];
04129 
04130       memset(pclient, 0, sizeof(BUFFER_CLIENT));
04131       /* use client name previously set by bm_set_name */
04132       cm_get_client_info(pclient->name);
04133       if (pclient->name[0] == 0)
04134          strcpy(pclient->name, "unknown");
04135       pclient->pid = ss_getpid();
04136       pclient->tid = ss_gettid();
04137       pclient->thandle = ss_getthandle();
04138 
04139       ss_suspend_get_port(&pclient->port);
04140 
04141       pclient->read_pointer = pheader->write_pointer;
04142       pclient->last_activity = ss_millitime();
04143 
04144       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
04145 
04146       bm_unlock_buffer(handle + 1);
04147 
04148       /* setup _buffer entry */
04149       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
04150       _buffer[handle].attached = TRUE;
04151       _buffer[handle].shm_handle = shm_handle;
04152       _buffer[handle].callback = FALSE;
04153 
04154       /* remember to which connection acutal buffer belongs */
04155       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
04156          _buffer[handle].index = rpc_get_server_acception();
04157       else
04158          _buffer[handle].index = ss_gettid();
04159 
04160       *buffer_handle = (handle + 1);
04161 
04162       /* initialize buffer counters */
04163       bm_init_buffer_counters(handle + 1);
04164 
04165       /* setup dispatcher for receive events */
04166       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
04167 
04168       if (shm_created)
04169          return BM_CREATED;
04170    }
04171 #endif                          /* LOCAL_ROUTINES */
04172 
04173    return BM_SUCCESS;
04174 }
04175 
04176 /********************************************************************/
04177 /**
04178 Closes an event buffer previously opened with bm_open_buffer().
04179 @param buffer_handle buffer handle
04180 @return BM_SUCCESS, BM_INVALID_HANDLE
04181 */
04182 INT bm_close_buffer(INT buffer_handle)
04183 {
04184    if (rpc_is_remote())
04185       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
04186 
04187 #ifdef LOCAL_ROUTINES
04188    {
04189       BUFFER_CLIENT *pclient;
04190       BUFFER_HEADER *pheader;
04191       INT i, j, idx, destroy_flag;
04192 
04193       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04194          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
04195          return BM_INVALID_HANDLE;
04196       }
04197 
04198       /* check if buffer got already closed */
04199       if (!_buffer[buffer_handle - 1].attached) {
04200          return BM_SUCCESS;
04201       }
04202 
04203       /*
04204          Check if buffer was opened by current thread. This is necessary
04205          in the server process where one thread may not close the buffer
04206          of other threads.
04207        */
04208 
04209       idx = bm_validate_client_index(&_buffer[buffer_handle - 1]);
04210       pheader = _buffer[buffer_handle - 1].buffer_header;
04211 
04212       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04213           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04214          return BM_INVALID_HANDLE;
04215 
04216       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04217           _buffer[buffer_handle - 1].index != ss_gettid())
04218          return BM_INVALID_HANDLE;
04219 
04220       if (!_buffer[buffer_handle - 1].attached) {
04221          /* don't produce error, since bm_close_all_buffers() might want to close an
04222             already closed buffer */
04223          return BM_SUCCESS;
04224       }
04225 
04226       /* delete all requests for this buffer */
04227       for (i = 0; i < _request_list_entries; i++)
04228          if (_request_list[i].buffer_handle == buffer_handle)
04229             bm_delete_request(i);
04230 
04231       /* first lock buffer */
04232       bm_lock_buffer(buffer_handle);
04233 
04234       /* mark entry in _buffer as empty */
04235       _buffer[buffer_handle - 1].attached = FALSE;
04236 
04237       /* clear entry from client structure in buffer header */
04238       memset(&(pheader->client[idx]), 0, sizeof(BUFFER_CLIENT));
04239 
04240       /* calculate new max_client_index entry */
04241       for (i = MAX_CLIENTS - 1; i >= 0; i--)
04242          if (pheader->client[i].pid != 0)
04243             break;
04244       pheader->max_client_index = i + 1;
04245 
04246       /* count new number of clients */
04247       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04248          if (pheader->client[i].pid != 0)
04249             j++;
04250       pheader->num_clients = j;
04251 
04252       destroy_flag = (pheader->num_clients == 0);
04253 
04254       /* free cache */
04255       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04256          M_FREE(_buffer[buffer_handle - 1].read_cache);
04257       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04258          M_FREE(_buffer[buffer_handle - 1].write_cache);
04259 
04260       /* check if anyone is waiting and wake him up */
04261       pclient = pheader->client;
04262 
04263       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04264          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04265             ss_resume(pclient->port, "B  ");
04266 
04267       /* unmap shared memory, delete it if we are the last */
04268       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04269                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04270 
04271       /* unlock buffer */
04272       bm_unlock_buffer(buffer_handle);
04273 
04274       /* delete mutex */
04275       ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04276 
04277       /* update _buffer_entries */
04278       if (buffer_handle == _buffer_entries)
04279          _buffer_entries--;
04280 
04281       if (_buffer_entries > 0)
04282          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04283       else {
04284          M_FREE(_buffer);
04285          _buffer = NULL;
04286       }
04287    }
04288 #endif                          /* LOCAL_ROUTINES */
04289 
04290    return BM_SUCCESS;
04291 }
04292 
04293 /********************************************************************/
04294 /**
04295 Close all open buffers
04296 @return BM_SUCCESS
04297 */
04298 INT bm_close_all_buffers(void)
04299 {
04300    if (rpc_is_remote())
04301       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04302 
04303 #ifdef LOCAL_ROUTINES
04304    {
04305       INT i;
04306 
04307       for (i = _buffer_entries; i > 0; i--)
04308          bm_close_buffer(i);
04309    }
04310 #endif                          /* LOCAL_ROUTINES */
04311 
04312    return BM_SUCCESS;
04313 }
04314 
04315 /**dox***************************************************************/
04316                                                                                                              /** @} *//* end of bmfunctionc */
04317 
04318 /**dox***************************************************************/
04319 /** @addtogroup cmfunctionc
04320  *
04321  *  @{  */
04322 
04323 /*-- Watchdog routines ---------------------------------------------*/
04324 #ifdef LOCAL_ROUTINES
04325 
04326 /********************************************************************/
04327 /**
04328 Called at periodic intervals, checks if all clients are
04329 alive. If one process died, its client entries are cleaned up.
04330 @param dummy unused!
04331 */
04332 void cm_watchdog(int dummy)
04333 {
04334    BUFFER_HEADER *pheader;
04335    BUFFER_CLIENT *pbclient, *pbctmp;
04336    DATABASE_HEADER *pdbheader;
04337    DATABASE_CLIENT *pdbclient;
04338    KEY *pkey;
04339    DWORD actual_time, interval;
04340    INT client_pid;
04341    INT i, j, k, nc, status;
04342    BOOL bDeleted, time_changed, wrong_interval;
04343    char str[256];
04344 
04345    i = dummy; /* avoid compiler warning */
04346 
04347    /* return immediately if watchdog has been disabled in meantime */
04348    if (!_call_watchdog)
04349       return;
04350 
04351    /* tell system services that we are in async mode ... */
04352    ss_set_async_flag(TRUE);
04353 
04354    /* Calculate the time since last watchdog call. Kill clients if they
04355       are inactive for more than the timeout they specified */
04356    actual_time = ss_millitime();
04357    if (_watchdog_last_called == 0)
04358       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04359    interval = actual_time - _watchdog_last_called;
04360 
04361    /* check if system time has been changed more than 10 min */
04362    time_changed = interval > 600000;
04363    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL || interval > 1.2 * WATCHDOG_INTERVAL;
04364 
04365    if (time_changed)
04366       cm_msg(MINFO, "cm_watchdog",
04367              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04368              _watchdog_last_called, actual_time, interval);
04369 
04370    /* check buffers */
04371    for (i = 0; i < _buffer_entries; i++)
04372       if (_buffer[i].attached) {
04373          /* update the last_activity entry to show that we are alive */
04374          pheader = _buffer[i].buffer_header;
04375          pbclient = pheader->client;
04376          pbclient[bm_validate_client_index(&_buffer[i])].last_activity = actual_time;
04377 
04378          /* don't check other clients if interval is stange */
04379          if (wrong_interval)
04380             continue;
04381 
04382          /* now check other clients */
04383          for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04384             /* If client process has no activity, clear its buffer entry. */
04385             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04386                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04387                bm_lock_buffer(i + 1);
04388                str[0] = 0;
04389 
04390                /* now make again the check with the buffer locked */
04391                actual_time = ss_millitime();
04392                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04393                    actual_time > pbclient->last_activity &&
04394                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04395                   sprintf(str,
04396                           "Client \'%s\' on buffer \'%s\' removed by cm_watchdog (idle %1.1lfs,TO %1.0lfs)",
04397                           pbclient->name, pheader->name,
04398                           (actual_time - pbclient->last_activity) / 1000.0,
04399                           pbclient->watchdog_timeout / 1000.0);
04400 
04401                   /* clear entry from client structure in buffer header */
04402                   memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04403 
04404                   /* calculate new max_client_index entry */
04405                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04406                      if (pheader->client[k].pid != 0)
04407                         break;
04408                   pheader->max_client_index = k + 1;
04409 
04410                   /* count new number of clients */
04411                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04412                      if (pheader->client[k].pid != 0)
04413                         nc++;
04414                   pheader->num_clients = nc;
04415 
04416                   /* check if anyone is waiting and wake him up */
04417                   pbctmp = pheader->client;
04418 
04419                   for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04420                      if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04421                         ss_resume(pbctmp->port, "B  ");
04422 
04423                }
04424 
04425                bm_unlock_buffer(i + 1);
04426 
04427                /* display info message after unlocking buffer */
04428                if (str[0])
04429                   cm_msg(MINFO, "cm_watchdog", str);
04430             }
04431       }
04432 
04433    /* check online databases */
04434    for (i = 0; i < _database_entries; i++)
04435       if (_database[i].attached) {
04436          /* update the last_activity entry to show that we are alive */
04437          pdbheader = _database[i].database_header;
04438          pdbclient = pdbheader->client;
04439          pdbclient[_database[i].client_index].last_activity = actual_time;
04440 
04441          /* don't check other clients if interval is stange */
04442          if (wrong_interval)
04443             continue;
04444 
04445          /* now check other clients */
04446          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04447             /* If client process has no activity, clear its buffer entry. */
04448             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04449                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04450                client_pid = pdbclient->tid;
04451                bDeleted = FALSE;
04452                db_lock_database(i + 1);
04453                str[0] = 0;
04454 
04455                /* now make again the check with the buffer locked */
04456                actual_time = ss_millitime();
04457                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04458                    actual_time > pdbclient->last_activity &&
04459                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04460                   sprintf(str,
04461                           "Client \'%s\' (PID %d) on buffer \'%s\' removed by cm_watchdog (idle %1.1lfs,TO %1.0lfs)",
04462                           pdbclient->name, client_pid, pdbheader->name,
04463                           (actual_time - pdbclient->last_activity) / 1000.0,
04464                           pdbclient->watchdog_timeout / 1000.0);
04465 
04466                   /* decrement notify_count for open records and clear exclusive mode */
04467                   for (k = 0; k < pdbclient->max_index; k++)
04468                      if (pdbclient->open_record[k].handle) {
04469                         pkey = (KEY *) ((char *) pdbheader + pdbclient->open_record[k].handle);
04470                         if (pkey->notify_count > 0)
04471                            pkey->notify_count--;
04472 
04473                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04474                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04475                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04476                      }
04477 
04478                   /* clear entry from client structure in buffer header */
04479                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04480 
04481                   /* calculate new max_client_index entry */
04482                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04483                      if (pdbheader->client[k].pid != 0)
04484                         break;
04485                   pdbheader->max_client_index = k + 1;
04486 
04487                   /* count new number of clients */
04488                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04489                      if (pdbheader->client[k].pid != 0)
04490                         nc++;
04491                   pdbheader->num_clients = nc;
04492                   bDeleted = TRUE;
04493                }
04494 
04495                /* delete client entry before unlocking db */
04496                if (bDeleted) {
04497                   status = cm_delete_client_info(i + 1, client_pid);
04498                   if (status != CM_SUCCESS)
04499                      cm_msg(MERROR, "cm_watchdog", "cannot delete client info for client \'%s\', pid %d from buffer \'%s\', status %d", pdbclient->name, client_pid, pdbheader->name, status);               }
04500 
04501                db_unlock_database(i + 1);
04502 
04503                /* display info message after unlocking db */
04504                if (str[0])
04505                   cm_msg(MINFO, "cm_watchdog", str);
04506             }
04507       }
04508 
04509    _watchdog_last_called = actual_time;
04510 
04511    ss_set_async_flag(FALSE);
04512 
04513    /* Schedule next watchdog call */
04514    if (_call_watchdog)
04515       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04516 }
04517 
04518 /********************************************************************/
04519 /**
04520 Temporarily disable watchdog calling. Used for tape IO
04521 not to interrupt lengthy operations like mount.
04522 @param flag FALSE for disable, TRUE for re-enable
04523 @return CM_SUCCESS
04524 */
04525 INT cm_enable_watchdog(BOOL flag)
04526 {
04527    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
04528    static BOOL call_flag = FALSE;
04529 
04530    if (flag) {
04531       if (call_flag)
04532          cm_set_watchdog_params(TRUE, timeout);
04533    } else {
04534       call_flag = _call_watchdog;
04535       timeout = _watchdog_timeout;
04536       if (call_flag)
04537          cm_set_watchdog_params(FALSE, 0);
04538    }
04539 
04540    return CM_SUCCESS;
04541 }
04542 
04543 #endif                          /* local routines */
04544 
04545 /********************************************************************/
04546 /**
04547 Shutdown (exit) other MIDAS client
04548 @param name           Client name or "all" for all clients
04549 @param bUnique        If true, look for the exact client name.
04550                       If false, look for namexxx where xxx is
04551                       a any number.
04552 
04553 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY
04554 */
04555 INT cm_shutdown(char *name, BOOL bUnique)
04556 {
04557    INT status, return_status, i, size;
04558    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
04559    KEY key;
04560    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
04561    INT port;
04562    DWORD start_time;
04563 
04564    cm_get_experiment_database(&hDB, &hKeyClient);
04565 
04566    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04567    if (status != DB_SUCCESS)
04568       return DB_NO_KEY;
04569 
04570    return_status = CM_NO_CLIENT;
04571 
04572    /* loop over all clients */
04573    for (i = 0;; i++) {
04574       status = db_enum_key(hDB, hKey, i, &hSubkey);
04575       if (status == DB_NO_MORE_SUBKEYS)
04576          break;
04577 
04578       /* don't shutdown ourselves */
04579       if (hSubkey == hKeyClient)
04580          continue;
04581 
04582       if (status == DB_SUCCESS) {
04583          db_get_key(hDB, hSubkey, &key);
04584 
04585          /* contact client */
04586          size = sizeof(client_name);
04587          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04588 
04589          if (!bUnique)
04590             client_name[strlen(name)] = 0;      /* strip number */
04591 
04592          /* check if individual client */
04593          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
04594             continue;
04595 
04596          size = sizeof(port);
04597          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
04598 
04599          size = sizeof(remote_host);
04600          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
04601 
04602          /* client found -> connect to its server port */
04603          status = rpc_client_connect(remote_host, port, client_name, &hConn);
04604          if (status != RPC_SUCCESS) {
04605             return_status = CM_NO_CLIENT;
04606             sprintf(str, "cannot connect to client %s on host %s, port %d", client_name, remote_host, port);
04607             cm_msg(MERROR, "cm_shutdown", str);
04608          } else {
04609             /* call disconnect with shutdown=TRUE */
04610             rpc_client_disconnect(hConn, TRUE);
04611 
04612             /* wait until client has shut down */
04613             start_time = ss_millitime();
04614             do {
04615                ss_sleep(100);
04616                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
04617             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
04618 
04619             if (status == DB_SUCCESS) {
04620                cm_msg(MINFO, "cm_shutdown",
04621                       "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
04622                       client_name);
04623                return_status = CM_NO_CLIENT;
04624             } else {
04625                return_status = CM_SUCCESS;
04626                i--;
04627             }
04628          }
04629       }
04630    }
04631 
04632    return return_status;
04633 }
04634 
04635 /********************************************************************/
04636 /**
04637 Check if a MIDAS client exists in current experiment
04638 @param    name            Client name
04639 @param    bUnique         If true, look for the exact client name.
04640                           If false, look for namexxx where xxx is
04641                           a any number
04642 @return   CM_SUCCESS, CM_NO_CLIENT
04643 */
04644 INT cm_exist(char *name, BOOL bUnique)
04645 {
04646    INT status, i, size;
04647    HNDLE hDB, hKeyClient, hKey, hSubkey;
04648    char client_name[NAME_LENGTH];
04649 
04650    if (rpc_is_remote())
04651       return rpc_call(RPC_CM_EXIST, name, bUnique);
04652 
04653    cm_get_experiment_database(&hDB, &hKeyClient);
04654 
04655    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04656    if (status != DB_SUCCESS)
04657       return DB_NO_KEY;
04658 
04659    /* loop over all clients */
04660    for (i = 0;; i++) {
04661       status = db_enum_key(hDB, hKey, i, &hSubkey);
04662       if (status == DB_NO_MORE_SUBKEYS)
04663          break;
04664 
04665       if (hSubkey == hKeyClient)
04666          continue;
04667 
04668       if (status == DB_SUCCESS) {
04669          /* get client name */
04670          size = sizeof(client_name);
04671          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04672 
04673          if (equal_ustring(client_name, name))
04674             return CM_SUCCESS;
04675 
04676          if (!bUnique) {
04677             client_name[strlen(name)] = 0;      /* strip number */
04678             if (equal_ustring(client_name, name))
04679                return CM_SUCCESS;
04680          }
04681       }
04682    }
04683 
04684    return CM_NO_CLIENT;
04685 }
04686 
04687 /********************************************************************/
04688 /**
04689 Remove hanging clients independent of their watchdog
04690            timeout.
04691 
04692 Since this function does not obey the client watchdog
04693 timeout, it should be only called to remove clients which
04694 have their watchdog checking turned off or which are
04695 known to be dead. The normal client removement is done
04696 via cm_watchdog().
04697 
04698 Currently (Sept. 02) there are two applications for that:
04699 -# The ODBEdit command "cleanup", which can be used to
04700 remove clients which have their watchdog checking off,
04701 like the analyzer started with the "-d" flag for a
04702 debugging session.
04703 -# The frontend init code to remove previous frontends.
04704 This can be helpful if a frontend dies. Normally,
04705 one would have to wait 60 sec. for a crashed frontend
04706 to be removed. Only then one can start again the
04707 frontend. Since the frontend init code contains a
04708 call to cm_cleanup(<frontend_name>), one can restart
04709 a frontend immediately.
04710 
04711 Added ignore_timeout on Nov.03. A logger might have an
04712 increased tiemout of up to 60 sec. because of tape
04713 operations. If ignore_timeout is FALSE, the logger is
04714 then not killed if its inactivity is less than 60 sec.,
04715 while in the previous implementation it was always
04716 killed after 2*WATCHDOG_INTERVAL.
04717 @param    client_name      Client name, if zero check all clients
04718 @param    ignore_timeout   If TRUE, ignore a possible increased
04719                            timeout defined by each client.
04720 @return   CM_SUCCESS
04721 */
04722 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
04723 {
04724    if (rpc_is_remote())
04725       return rpc_call(RPC_CM_CLEANUP, client_name);
04726 
04727 #ifdef LOCAL_ROUTINES
04728    {
04729       BUFFER_HEADER *pheader = NULL;
04730       BUFFER_CLIENT *pbclient, *pbctmp;
04731       DATABASE_HEADER *pdbheader;
04732       DATABASE_CLIENT *pdbclient;
04733       KEY *pkey;
04734       INT client_pid;
04735       INT i, j, k, status, nc;
04736       BOOL bDeleted;
04737       char str[256];
04738       DWORD interval;
04739 
04740       /* check buffers */
04741       for (i = 0; i < _buffer_entries; i++)
04742          if (_buffer[i].attached) {
04743             /* update the last_activity entry to show that we are alive */
04744             pheader = _buffer[i].buffer_header;
04745             pbclient = pheader->client;
04746             pbclient[bm_validate_client_index(&_buffer[i])].last_activity = ss_millitime();
04747 
04748             /* now check other clients */
04749             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04750                if (j != _buffer[i].client_index && pbclient->pid &&
04751                    (client_name == NULL || client_name[0] == 0
04752                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
04753                   if (ignore_timeout)
04754                      interval = 2 * WATCHDOG_INTERVAL;
04755                   else
04756                      interval = pbclient->watchdog_timeout;
04757 
04758                   /* If client process has no activity, clear its buffer entry. */
04759                   if (ss_millitime() - pbclient->last_activity > interval) {
04760                      bm_lock_buffer(i + 1);
04761                      str[0] = 0;
04762 
04763                      /* now make again the check with the buffer locked */
04764                      if (ss_millitime() - pbclient->last_activity > interval) {
04765                         sprintf(str,
04766                                 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs,TO %1.0lfs)",
04767                                 pbclient->name, pheader->name,
04768                                 (ss_millitime() - pbclient->last_activity) / 1000.0, interval / 1000.0);
04769 
04770                         /* clear entry from client structure in buffer header */
04771                         memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04772 
04773                         /* calculate new max_client_index entry */
04774                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04775                            if (pheader->client[k].pid != 0)
04776                               break;
04777                         pheader->max_client_index = k + 1;
04778 
04779                         /* count new number of clients */
04780                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04781                            if (pheader->client[k].pid != 0)
04782                               nc++;
04783                         pheader->num_clients = nc;
04784 
04785                         /* check if anyone is waiting and wake him up */
04786                         pbctmp = pheader->client;
04787 
04788                         for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04789                            if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04790                               ss_resume(pbctmp->port, "B  ");
04791 
04792                      }
04793 
04794                      bm_unlock_buffer(i + 1);
04795 
04796                      /* display info message after unlocking buffer */
04797                      if (str[0])
04798                         cm_msg(MINFO, "cm_cleanup", str);
04799 
04800                      /* go again through whole list */
04801                      j = 0;
04802                   }
04803                }
04804          }
04805 
04806       /* check online databases */
04807       for (i = 0; i < _database_entries; i++)
04808          if (_database[i].attached) {
04809             /* update the last_activity entry to show that we are alive */
04810             db_lock_database(i + 1);
04811 
04812             pdbheader = _database[i].database_header;
04813             pdbclient = pdbheader->client;
04814             pdbclient[_database[i].client_index].last_activity = ss_millitime();
04815 
04816             /* now check other clients */
04817             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04818                if (j != _database[i].client_index && pdbclient->pid &&
04819                    (client_name == NULL || client_name[0] == 0
04820                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
04821                   client_pid = pdbclient->tid;
04822                   if (ignore_timeout)
04823                      interval = 2 * WATCHDOG_INTERVAL;
04824                   else
04825                      interval = pdbclient->watchdog_timeout;
04826 
04827                   /* If client process has no activity, clear its buffer entry. */
04828 
04829                   if (ss_millitime() - pdbclient->last_activity > interval) {
04830                      bDeleted = FALSE;
04831                      str[0] = 0;
04832 
04833                      /* now make again the check with the buffer locked */
04834                      if (ss_millitime() - pdbclient->last_activity > interval) {
04835                         sprintf(str,
04836                                 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs,TO %1.0lfs)",
04837                                 pdbclient->name, pdbheader->name,
04838                                 (ss_millitime() - pdbclient->last_activity) / 1000.0, interval / 1000.0);
04839 
04840                         /* decrement notify_count for open records and clear exclusive mode */
04841                         for (k = 0; k < pdbclient->max_index; k++)
04842                            if (pdbclient->open_record[k].handle) {
04843                               pkey = (KEY *) ((char *) pdbheader + pdbclient->open_record[k].handle);
04844                               if (pkey->notify_count > 0)
04845                                  pkey->notify_count--;
04846 
04847                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04848                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
04849                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04850                            }
04851 
04852                         /* clear entry from client structure in buffer header */
04853                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04854 
04855                         /* calculate new max_client_index entry */
04856                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
04857                            if (pdbheader->client[k].pid != 0)
04858                               break;
04859                         pdbheader->max_client_index = k + 1;
04860 
04861                         /* count new number of clients */
04862                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04863                            if (pheader->client[k].pid != 0)
04864                               nc++;
04865                         pdbheader->num_clients = nc;
04866 
04867                         bDeleted = TRUE;
04868                      }
04869 
04870 
04871                      /* delete client entry after unlocking db */
04872                      if (bDeleted) {
04873                         db_unlock_database(i + 1);
04874 
04875                         /* display info message after unlocking buffer */
04876                         cm_msg(MINFO, "cm_cleanup", str);
04877 
04878                         status = cm_delete_client_info(i + 1, client_pid);
04879                         if (status != CM_SUCCESS)
04880                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
04881 
04882                         /* re-lock database */
04883                         db_lock_database(i + 1);
04884                         pdbheader = _database[i].database_header;
04885                         pdbclient = pdbheader->client;
04886 
04887                         /* go again though whole list */
04888                         j = 0;
04889                      }
04890                   }
04891                }
04892 
04893             db_unlock_database(i + 1);
04894          }
04895 
04896    }
04897 #endif                          /* LOCAL_ROUTINES */
04898 
04899    return CM_SUCCESS;
04900 }
04901 
04902 /**dox***************************************************************/
04903 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04904 
04905 /********************************************************************/
04906 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
04907 /********************************************************************\
04908 
04909   Routine: bm_buffer_info
04910 
04911   Purpose: Copies the current buffer header referenced by buffer_handle
04912            into the *buffer_header structure which must be supplied
04913            by the calling routine.
04914 
04915   Input:
04916     INT buffer_handle       Handle of the buffer to get the header from
04917 
04918   Output:
04919     BUFFER_HEADER *buffer_header   Destination address which gets a copy
04920                                    of the buffer header structure.
04921 
04922   Function value:
04923     BM_SUCCESS              Successful completion
04924     BM_INVALID_HANDLE       Buffer handle is invalid
04925     RPC_NET_ERROR           Network error
04926 
04927 \********************************************************************/
04928 {
04929    if (rpc_is_remote())
04930       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
04931 
04932 #ifdef LOCAL_ROUTINES
04933 
04934    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04935       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04936       return BM_INVALID_HANDLE;
04937    }
04938 
04939    if (!_buffer[buffer_handle - 1].attached) {
04940       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
04941       return BM_INVALID_HANDLE;
04942    }
04943 
04944    bm_lock_buffer(buffer_handle);
04945 
04946    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
04947 
04948    bm_unlock_buffer(buffer_handle);
04949 
04950 #endif                          /* LOCAL_ROUTINES */
04951 
04952    return BM_SUCCESS;
04953 }
04954 
04955 /********************************************************************/
04956 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
04957 /********************************************************************\
04958 
04959   Routine: bm_get_buffer_level
04960 
04961   Purpose: Return number of bytes in buffer or in cache
04962 
04963   Input:
04964     INT buffer_handle       Handle of the buffer to get the info
04965 
04966   Output:
04967     INT *n_bytes              Number of bytes in buffer
04968 
04969   Function value:
04970     BM_SUCCESS              Successful completion
04971     BM_INVALID_HANDLE       Buffer handle is invalid
04972     RPC_NET_ERROR           Network error
04973 
04974 \********************************************************************/
04975 {
04976    if (rpc_is_remote())
04977       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
04978 
04979 #ifdef LOCAL_ROUTINES
04980    {
04981       BUFFER *pbuf;
04982       BUFFER_HEADER *pheader;
04983       BUFFER_CLIENT *pclient;
04984 
04985       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04986          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04987          return BM_INVALID_HANDLE;
04988       }
04989 
04990       pbuf = &_buffer[buffer_handle - 1];
04991       pheader = pbuf->buffer_header;
04992 
04993       if (!pbuf->attached) {
04994          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
04995          return BM_INVALID_HANDLE;
04996       }
04997 
04998       bm_lock_buffer(buffer_handle);
04999 
05000       pclient = &(pheader->client[bm_validate_client_index(pbuf)]);
05001 
05002       *n_bytes = pheader->write_pointer - pclient->read_pointer;
05003       if (*n_bytes < 0)
05004          *n_bytes += pheader->size;
05005 
05006       bm_unlock_buffer(buffer_handle);
05007 
05008       /* add bytes in cache */
05009       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
05010          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
05011    }
05012 #endif                          /* LOCAL_ROUTINES */
05013 
05014    return BM_SUCCESS;
05015 }
05016 
05017 
05018 
05019 #ifdef LOCAL_ROUTINES
05020 
05021 /********************************************************************/
05022 INT bm_lock_buffer(INT buffer_handle)
05023 /********************************************************************\
05024 
05025   Routine: bm_lock_buffer
05026 
05027   Purpose: Lock a buffer for exclusive access via system mutex calls.
05028 
05029   Input:
05030     INT    bufer_handle     Handle to the buffer to lock
05031   Output:
05032     none
05033 
05034   Function value:
05035     BM_SUCCESS              Successful completion
05036     BM_INVALID_HANDLE       Buffer handle is invalid
05037 
05038 \********************************************************************/
05039 {
05040    int status;
05041 
05042    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05043       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
05044       return BM_INVALID_HANDLE;
05045    }
05046 
05047    status = ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 5 * 60 * 1000);
05048 
05049    if (status != SS_SUCCESS) {
05050       cm_msg(MERROR, "bm_lock_buffer",
05051              "Cannot lock buffer handle %d, ss_mutex_wait_for() status %d", buffer_handle, status);
05052       abort();
05053       return BM_INVALID_HANDLE;
05054    }
05055 
05056    return BM_SUCCESS;
05057 }
05058 
05059 /********************************************************************/
05060 INT bm_unlock_buffer(INT buffer_handle)
05061 /********************************************************************\
05062 
05063   Routine: bm_unlock_buffer
05064 
05065   Purpose: Unlock a buffer via system mutex calls.
05066 
05067   Input:
05068     INT    bufer_handle     Handle to the buffer to lock
05069   Output:
05070     none
05071 
05072   Function value:
05073     BM_SUCCESS              Successful completion
05074     BM_INVALID_HANDLE       Buffer handle is invalid
05075 
05076 \********************************************************************/
05077 {
05078    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05079       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
05080       return BM_INVALID_HANDLE;
05081    }
05082 
05083    ss_mutex_release(_buffer[buffer_handle - 1].mutex);
05084    return BM_SUCCESS;
05085 }
05086 
05087 #endif                          /* LOCAL_ROUTINES */
05088 
05089 /********************************************************************/
05090 INT bm_init_buffer_counters(INT buffer_handle)
05091 /********************************************************************\
05092 
05093   Routine: bm_init_event_counters
05094 
05095   Purpose: Initialize counters for a specific buffer. This routine
05096            should be called at the beginning of a run.
05097 
05098   Input:
05099     INT    buffer_handle    Handle to the buffer to be
05100                             initialized.
05101   Output:
05102     none
05103 
05104   Function value:
05105     BM_SUCCESS              Successful completion
05106     BM_INVALID_HANDLE       Buffer handle is invalid
05107 
05108 \********************************************************************/
05109 {
05110    if (rpc_is_remote())
05111       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
05112 
05113 #ifdef LOCAL_ROUTINES
05114 
05115    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05116       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d", buffer_handle);
05117       return BM_INVALID_HANDLE;
05118    }
05119 
05120    if (!_buffer[buffer_handle - 1].attached) {
05121       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d", buffer_handle);
05122       return BM_INVALID_HANDLE;
05123    }
05124 
05125    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
05126    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
05127 
05128 #endif                          /* LOCAL_ROUTINES */
05129 
05130    return BM_SUCCESS;
05131 }
05132 
05133 /**dox***************************************************************/
05134 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05135 
05136 /**dox***************************************************************/
05137                                                                                                              /** @} *//* end of cmfunctionc */
05138 
05139 /**dox***************************************************************/
05140 /** @addtogroup bmfunctionc
05141  *
05142  *  @{  */
05143 
05144 /********************************************************************/
05145 /**
05146 Modifies buffer cache size.
05147 Without a buffer cache, events are copied to/from the shared
05148 memory event by event.
05149 
05150 To protect processed from accessing the shared memory simultaneously,
05151 semaphores are used. Since semaphore operations are CPU consuming (typically
05152 50-100us) this can slow down the data transfer especially for small events.
05153 By using a cache the number of semaphore operations is reduced dramatically.
05154 Instead writing directly to the shared memory, the events are copied to a
05155 local cache buffer. When this buffer is full, it is copied to the shared
05156 memory in one operation. The same technique can be used when receiving events.
05157 
05158 The drawback of this method is that the events have to be copied twice, once to the
05159 cache and once from the cache to the shared memory. Therefore it can happen that the
05160 usage of a cache even slows down data throughput on a given environment (computer
05161 type, OS type, event size).
05162 The cache size has therefore be optimized manually to maximize data throughput.
05163 @param buffer_handle buffer handle obtained via bm_open_buffer()
05164 @param read_size cache size for reading events in bytes, zero for no cache
05165 @param write_size cache size for writing events in bytes, zero for no cache
05166 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
05167 */
05168 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
05169 /*------------------------------------------------------------------*/
05170 {
05171    if (rpc_is_remote())
05172       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
05173 
05174 #ifdef LOCAL_ROUTINES
05175    {
05176       BUFFER *pbuf;
05177 
05178       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05179          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05180          return BM_INVALID_HANDLE;
05181       }
05182 
05183       if (!_buffer[buffer_handle - 1].attached) {
05184          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05185          return BM_INVALID_HANDLE;
05186       }
05187 
05188       if (read_size < 0 || read_size > 1E6) {
05189          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
05190          return BM_INVALID_PARAM;
05191       }
05192 
05193       if (write_size < 0 || write_size > 1E6) {
05194          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
05195          return BM_INVALID_PARAM;
05196       }
05197 
05198       /* manage read cache */
05199       pbuf = &_buffer[buffer_handle - 1];
05200 
05201       if (pbuf->read_cache_size > 0)
05202          M_FREE(pbuf->read_cache);
05203 
05204       if (read_size > 0) {
05205          pbuf->read_cache = (char *) M_MALLOC(read_size);
05206          if (pbuf->read_cache == NULL) {
05207             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate cache buffer");
05208             return BM_NO_MEMORY;
05209          }
05210       }
05211 
05212       pbuf->read_cache_size = read_size;
05213       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05214 
05215       /* manage write cache */
05216       if (pbuf->write_cache_size > 0)
05217          M_FREE(pbuf->write_cache);
05218 
05219       if (write_size > 0) {
05220          pbuf->write_cache = (char *) M_MALLOC(write_size);
05221          if (pbuf->write_cache == NULL) {
05222             cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate cache buffer");
05223             return BM_NO_MEMORY;
05224          }
05225       }
05226 
05227       pbuf->write_cache_size = write_size;
05228       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05229 
05230    }
05231 #endif                          /* LOCAL_ROUTINES */
05232 
05233    return BM_SUCCESS;
05234 }
05235 
05236 /********************************************************************/
05237 /**
05238 Compose a Midas event header.
05239 An event header can usually be set-up manually or
05240 through this routine. If the data size of the event is not known when
05241 the header is composed, it can be set later with event_header->data-size = <...>
05242 Following structure is created at the beginning of an event
05243 \code
05244 typedef struct {
05245  short int     event_id;
05246  short int     trigger_mask;
05247  DWORD         serial_number;
05248  DWORD         time_stamp;
05249  DWORD         data_size;
05250 } EVENT_HEADER;
05251 
05252 char event[1000];
05253  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05254  *(event+sizeof(EVENT_HEADER)) = <...>
05255 \endcode
05256 @param event_header pointer to the event header
05257 @param event_id event ID of the event
05258 @param trigger_mask trigger mask of the event
05259 @param size size if the data part of the event in bytes
05260 @param serial serial number
05261 @return BM_SUCCESS
05262 */
05263 INT bm_compose_event(EVENT_HEADER * event_header,
05264                      short int event_id, short int trigger_mask, DWORD size, DWORD serial)
05265 {
05266    event_header->event_id = event_id;
05267    event_header->trigger_mask = trigger_mask;
05268    event_header->data_size = size;
05269    event_header->time_stamp = ss_time();
05270    event_header->serial_number = serial;
05271 
05272    return BM_SUCCESS;
05273 }
05274 
05275 
05276 /**dox***************************************************************/
05277 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05278 
05279 /********************************************************************/
05280 INT bm_add_event_request(INT buffer_handle, short int event_id,
05281                          short int trigger_mask,
05282                          INT sampling_type,
05283                          void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *), INT request_id)
05284 /********************************************************************\
05285 
05286   Routine:  bm_add_event_request
05287 
05288   Purpose:  Place a request for a specific event type in the client
05289             structure of the buffer refereced by buffer_handle.
05290 
05291   Input:
05292     INT          buffer_handle  Handle to the buffer where the re-
05293                                 quest should be placed in
05294 
05295     short int    event_id       Event ID      \
05296     short int    trigger_mask   Trigger mask  / Event specification
05297 
05298     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05299 
05300 
05301                  Note: to request all types of events, use
05302                    event_id = 0 (all others should be !=0 !)
05303                    trigger_mask = TRIGGER_ALL
05304                    sampling_typ = GET_ALL
05305 
05306 
05307     void         *func          Callback function
05308     INT          request_id     Request id (unique number assigned
05309                                 by bm_request_event)
05310 
05311   Output:
05312     none
05313 
05314   Function value:
05315     BM_SUCCESS              Successful completion
05316     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05317                             MIDAS.H should be increased.
05318     BM_INVALID_HANDLE       Buffer handle is invalid
05319     RPC_NET_ERROR           Network error
05320 
05321 \********************************************************************/
05322 {
05323    if (rpc_is_remote())
05324       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05325                       trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
05326 
05327 #ifdef LOCAL_ROUTINES
05328    {
05329       INT i;
05330       BUFFER_CLIENT *pclient;
05331 
05332       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05333          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d", buffer_handle);
05334          return BM_INVALID_HANDLE;
05335       }
05336 
05337       if (!_buffer[buffer_handle - 1].attached) {
05338          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d", buffer_handle);
05339          return BM_INVALID_HANDLE;
05340       }
05341 
05342       /* avoid callback/non callback requests */
05343       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05344          cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
05345          return BM_INVALID_MIXING;
05346       }
05347 
05348       /* get a pointer to the proper client structure */
05349       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05350                   client[bm_validate_client_index(&_buffer[buffer_handle - 1])]);
05351 
05352       /* lock buffer */
05353       bm_lock_buffer(buffer_handle);
05354 
05355       /* look for a empty request entry */
05356       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05357          if (!pclient->event_request[i].valid)
05358             break;
05359 
05360       if (i == MAX_EVENT_REQUESTS) {
05361          bm_unlock_buffer(buffer_handle);
05362          return BM_NO_MEMORY;
05363       }
05364 
05365       /* setup event_request structure */
05366       pclient->event_request[i].id = request_id;
05367       pclient->event_request[i].valid = TRUE;
05368       pclient->event_request[i].event_id = event_id;
05369       pclient->event_request[i].trigger_mask = trigger_mask;
05370       pclient->event_request[i].sampling_type = sampling_type;
05371 
05372       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05373 
05374       /* set callback flag in buffer structure */
05375       if (func != NULL)
05376          _buffer[buffer_handle - 1].callback = TRUE;
05377 
05378       /*
05379          Save the index of the last request in the list so that later only the
05380          requests 0..max_request_index-1 have to be searched through.
05381        */
05382 
05383       if (i + 1 > pclient->max_request_index)
05384          pclient->max_request_index = i + 1;
05385 
05386       bm_unlock_buffer(buffer_handle);
05387    }
05388 #endif                          /* LOCAL_ROUTINES */
05389 
05390    return BM_SUCCESS;
05391 }
05392 
05393 /**dox***************************************************************/
05394 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05395 
05396 /********************************************************************/
05397 /**
05398 Place an event request based on certain characteristics.
05399 Multiple event requests can be placed for each buffer, which
05400 are later identified by their request ID. They can contain different callback
05401 routines. Example see bm_open_buffer() and bm_receive_event()
05402 @param buffer_handle buffer handle obtained via bm_open_buffer()
05403 @param event_id event ID for requested events. Use EVENTID_ALL
05404 to receive events with any ID.
05405 @param trigger_mask trigger mask for requested events.
05406 The requested events must have at least one bit in its
05407 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05408 receive events with any trigger mask.
05409 @param sampling_type specifies how many events to receive.
05410 A value of GET_ALL receives all events which
05411 match the specified event ID and trigger mask. If the events are consumed slower
05412 than produced, the producer is automatically slowed down. A value of GET_SOME
05413 receives as much events as possible without slowing down the producer. GET_ALL is
05414 typically used by the logger, while GET_SOME is typically used by analyzers.
05415 @param request_id request ID returned by the function.
05416 This ID is passed to the callback routine and must
05417 be used in the bm_delete_request() routine.
05418 @param func allback routine which gets called when an event of the
05419 specified type is received.
05420 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05421 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05422 should be increased.
05423 */
05424 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05425                      short int trigger_mask,
05426                      INT sampling_type, HNDLE * request_id,
05427                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05428 {
05429    INT idx, status;
05430 
05431    /* allocate new space for the local request list */
05432    if (_request_list_entries == 0) {
05433       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05434       memset(_request_list, 0, sizeof(REQUEST_LIST));
05435       if (_request_list == NULL) {
05436          cm_msg(MERROR, "bm_request_event", "not enough memory to allocate request list buffer");
05437          return BM_NO_MEMORY;
05438       }
05439 
05440       _request_list_entries = 1;
05441       idx = 0;
05442    } else {
05443       /* check for a deleted entry */
05444       for (idx = 0; idx < _request_list_entries; idx++)
05445          if (!_request_list[idx].buffer_handle)
05446             break;
05447 
05448       /* if not found, create new one */
05449       if (idx == _request_list_entries) {
05450          _request_list = (REQUEST_LIST *) realloc(_request_list,
05451                                                   sizeof(REQUEST_LIST) * (_request_list_entries + 1));
05452          if (_request_list == NULL) {
05453             cm_msg(MERROR, "bm_request_event", "not enough memory to allocate request list buffer");
05454             return BM_NO_MEMORY;
05455          }
05456 
05457          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05458 
05459          _request_list_entries++;
05460       }
05461    }
05462 
05463    /* initialize request list */
05464    _request_list[idx].buffer_handle = buffer_handle;
05465    _request_list[idx].event_id = event_id;
05466    _request_list[idx].trigger_mask = trigger_mask;
05467    _request_list[idx].dispatcher = func;
05468 
05469    *request_id = idx;
05470 
05471    /* add request in buffer structure */
05472    status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, idx);
05473    if (status != BM_SUCCESS)
05474       return status;
05475 
05476    return BM_SUCCESS;
05477 }
05478 
05479 /********************************************************************/
05480 /**
05481 Delete a previously placed request for a specific event
05482 type in the client structure of the buffer refereced by buffer_handle.
05483 @param buffer_handle  Handle to the buffer where the re-
05484                                 quest should be placed in
05485 @param request_id     Request id returned by bm_request_event
05486 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR
05487 */
05488 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05489 {
05490    if (rpc_is_remote())
05491       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
05492 
05493 #ifdef LOCAL_ROUTINES
05494    {
05495       INT i, deleted;
05496       BUFFER_CLIENT *pclient;
05497 
05498       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05499          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d", buffer_handle);
05500          return BM_INVALID_HANDLE;
05501       }
05502 
05503       if (!_buffer[buffer_handle - 1].attached) {
05504          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d", buffer_handle);
05505          return BM_INVALID_HANDLE;
05506       }
05507 
05508       /* get a pointer to the proper client structure */
05509       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05510                   client[bm_validate_client_index(&_buffer[buffer_handle - 1])]);
05511 
05512       /* lock buffer */
05513       bm_lock_buffer(buffer_handle);
05514 
05515       /* check all requests and set to zero if matching */
05516       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
05517          if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
05518             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
05519             deleted++;
05520          }
05521 
05522       /* calculate new max_request_index entry */
05523       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
05524          if (pclient->event_request[i].valid)
05525             break;
05526 
05527       pclient->max_request_index = i + 1;
05528 
05529       /* caluclate new all_flag */
05530       pclient->all_flag = FALSE;
05531 
05532       for (i = 0; i < pclient->max_request_index; i++)
05533          if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
05534             pclient->all_flag = TRUE;
05535             break;
05536          }
05537 
05538       bm_unlock_buffer(buffer_handle);
05539 
05540       if (!deleted)
05541          return BM_NOT_FOUND;
05542    }
05543 #endif                          /* LOCAL_ROUTINES */
05544 
05545    return BM_SUCCESS;
05546 }
05547 
05548 /********************************************************************/
05549 /**
05550 Deletes an event request previously done with bm_request_event().
05551 When an event request gets deleted, events of that requested type are
05552 not received any more. When a buffer is closed via bm_close_buffer(), all
05553 event requests from that buffer are deleted automatically
05554 @param request_id request identifier given by bm_request_event()
05555 @return BM_SUCCESS, BM_INVALID_HANDLE
05556 */
05557 INT bm_delete_request(INT request_id)
05558 {
05559    if (request_id < 0 || request_id >= _request_list_entries)
05560       return BM_INVALID_HANDLE;
05561 
05562    /* remove request entry from buffer */
05563    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
05564 
05565    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
05566 
05567    return BM_SUCCESS;
05568 }
05569 
05570 #if 0 // currently not used
05571 static void bm_show_pointers(const BUFFER_HEADER * pheader)
05572 {
05573    int i;
05574    const BUFFER_CLIENT *pclient;
05575 
05576    pclient = pheader->client;
05577 
05578    printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
05579           pheader->write_pointer, pheader->size);
05580    for (i = 0; i < pheader->max_client_index; i++)
05581       if (pclient[i].pid) {
05582          printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
05583       }
05584 
05585    printf("done\n");
05586 }
05587 #endif
05588 
05589 static void bm_validate_client_pointers(BUFFER_HEADER * pheader, BUFFER_CLIENT * pclient)
05590 {
05591    if (pheader->read_pointer <= pheader->write_pointer) {
05592 
05593       if (pclient->read_pointer < pheader->read_pointer) {
05594          cm_msg(MINFO, "bm_validate_client_pointers",
05595                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05596                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05597 
05598          pclient->read_pointer = pheader->read_pointer;
05599       }
05600 
05601       if (pclient->read_pointer > pheader->write_pointer) {
05602          cm_msg(MINFO, "bm_validate_client_pointers",
05603                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05604                 pheader->name, pclient->read_pointer, pheader->write_pointer);
05605 
05606          pclient->read_pointer = pheader->write_pointer;
05607       }
05608 
05609    } else {
05610 
05611       if (pclient->read_pointer < 0) {
05612          cm_msg(MINFO, "bm_validate_client_pointers",
05613                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05614                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05615 
05616          pclient->read_pointer = pheader->read_pointer;
05617       }
05618 
05619       if (pclient->read_pointer >= pheader->size) {
05620          cm_msg(MINFO, "bm_validate_client_pointers",
05621                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05622                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05623 
05624          pclient->read_pointer = pheader->read_pointer;
05625       }
05626 
05627       if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
05628          cm_msg(MINFO, "bm_validate_client_pointers",
05629                 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d", pclient->name,
05630                 pheader->name, pclient->read_pointer, pheader->read_pointer);
05631 
05632          pclient->read_pointer = pheader->read_pointer;
05633       }
05634    }
05635 }
05636 
05637 #if 0 // currently not used
05638 static void bm_validate_pointers(BUFFER_HEADER * pheader)
05639 {
05640    BUFFER_CLIENT *pclient = pheader->client;
05641    int i;
05642 
05643    for (i = 0; i < pheader->max_client_index; i++)
05644       if (pclient[i].pid) {
05645          bm_validate_client_pointers(pheader, &pclient[i]);
05646       }
05647 }
05648 #endif
05649 
05650 static BOOL bm_update_read_pointer(const char *caller_name, BUFFER_HEADER * pheader)
05651 {
05652    BOOL did_move;
05653    int i;
05654    int min_rp;
05655    BUFFER_CLIENT *pclient;
05656 
05657    assert(caller_name);
05658    pclient = pheader->client;
05659 
05660    /* calculate global read pointer as "minimum" of client read pointers */
05661    min_rp = pheader->write_pointer;
05662 
05663    for (i = 0; i < pheader->max_client_index; i++)
05664       if (pclient[i].pid) {
05665 #ifdef DEBUG_MSG
05666          cm_msg(MDEBUG, caller_name, "bm_update_read_pointer: client %d rp=%d", i, pclient[i].read_pointer);
05667 #endif
05668          bm_validate_client_pointers(pheader, &pclient[i]);
05669 
05670          if (pheader->read_pointer <= pheader->write_pointer) {
05671             if (pclient[i].read_pointer < min_rp)
05672                min_rp = pclient[i].read_pointer;
05673          } else {
05674             if (pclient[i].read_pointer <= pheader->write_pointer) {
05675                if (pclient[i].read_pointer < min_rp)
05676                   min_rp = pclient[i].read_pointer;
05677             } else {
05678                int xptr = pclient[i].read_pointer - pheader->size;
05679                if (xptr < min_rp)
05680                   min_rp = xptr;
05681             }
05682          }
05683       }
05684 
05685    if (min_rp < 0)
05686       min_rp += pheader->size;
05687 
05688    assert(min_rp >= 0);
05689    assert(min_rp < pheader->size);
05690 
05691 #ifdef DEBUG_MSG
05692    if (min_rp == pheader->read_pointer)
05693       cm_msg(MDEBUG, caller_name, "bm_update_read_pointer -> wp=%d", pheader->write_pointer);
05694    else
05695       cm_msg(MDEBUG, caller_name, "bm_update_read_pointer -> wp=%d, rp %d -> %d, size=%d",
05696              pheader->write_pointer, pheader->read_pointer, min_rp, pheader->size);
05697 #endif
05698 
05699    did_move = (pheader->read_pointer != min_rp);
05700 
05701    pheader->read_pointer = min_rp;
05702 
05703    return did_move;
05704 }
05705 
05706 static void bm_wakeup_producers(const BUFFER_HEADER * pheader, const BUFFER_CLIENT * pc)
05707 {
05708    int i;
05709    int size;
05710    const BUFFER_CLIENT *pctmp = pheader->client;
05711 
05712    /*
05713       If read pointer has been changed, it may have freed up some space
05714       for waiting producers. So check if free space is now more than 50%
05715       of the buffer size and wake waiting producers.
05716     */
05717 
05718    size = pc->read_pointer - pheader->write_pointer;
05719    if (size <= 0)
05720       size += pheader->size;
05721 
05722    if (size >= pheader->size * 0.5)
05723       for (i = 0; i < pheader->max_client_index; i++, pctmp++)
05724          if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||  /* check if not own thread */
05725                                                           (pctmp->pid == ss_getpid()
05726                                                            && pctmp->tid != ss_gettid()))) {
05727 #ifdef DEBUG_MSG
05728             cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
05729                    pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05730 #endif
05731             ss_resume(pctmp->port, "B  ");
05732          }
05733 }
05734 
05735 static void bm_dispatch_event(int buffer_handle, EVENT_HEADER * pevent)
05736 {
05737    int i;
05738 
05739    /* call dispatcher */
05740    for (i = 0; i < _request_list_entries; i++)
05741       if (_request_list[i].buffer_handle == buffer_handle &&
05742           bm_match_event(_request_list[i].event_id, _request_list[i].trigger_mask, pevent)) {
05743          /* if event is fragmented, call defragmenter */
05744          if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 || (pevent->event_id & 0xF000) == EVENTID_FRAG)
05745             bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), _request_list[i].dispatcher);
05746          else
05747             _request_list[i].dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
05748       }
05749 }
05750 
05751 static void bm_dispatch_from_cache(BUFFER * pbuf, int buffer_handle)
05752 {
05753    EVENT_HEADER *pevent;
05754    int size;
05755 
05756    pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
05757    size = pevent->data_size + sizeof(EVENT_HEADER);
05758 
05759    /* correct size for DWORD boundary */
05760    size = ALIGN8(size);
05761 
05762    /* increment read pointer */
05763    pbuf->read_cache_rp += size;
05764 
05765    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
05766       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05767 
05768    bm_dispatch_event(buffer_handle, pevent);
05769 }
05770 
05771 static void bm_convert_event_header(EVENT_HEADER * pevent, int convert_flags)
05772 {
05773    /* now convert event header */
05774    if (convert_flags) {
05775       rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
05776       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING, convert_flags);
05777       rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING, convert_flags);
05778       rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING, convert_flags);
05779       rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING, convert_flags);
05780    }
05781 }
05782 
05783 static int bm_copy_from_cache(BUFFER * pbuf, void *destination, int max_size,
05784                               int *buf_size, int convert_flags)
05785 {
05786    int status;
05787    EVENT_HEADER *pevent;
05788    int size;
05789 
05790    pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
05791    size = pevent->data_size + sizeof(EVENT_HEADER);
05792 
05793    if (size > max_size) {
05794       memcpy(destination, pevent, max_size);
05795       cm_msg(MERROR, "bm_receive_event", "event size %d larger than buffer size %d", size, max_size);
05796       *buf_size = max_size;
05797       status = BM_TRUNCATED;
05798    } else {
05799       memcpy(destination, pevent, size);
05800       *buf_size = size;
05801       status = BM_SUCCESS;
05802    }
05803 
05804    bm_convert_event_header((EVENT_HEADER *) destination, convert_flags);
05805 
05806    /* correct size for DWORD boundary */
05807    size = ALIGN8(size);
05808 
05809    pbuf->read_cache_rp += size;
05810 
05811    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
05812       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05813 
05814    return status;
05815 }
05816 
05817 static int bm_read_cache_has_events(const BUFFER * pbuf)
05818 {
05819    if (pbuf->read_cache_size == 0)
05820       return 0;
05821 
05822    if (pbuf->read_cache_rp == pbuf->read_cache_wp)
05823       return 0;
05824 
05825    return 1;
05826 }
05827 
05828 static int bm_wait_for_free_space(int buffer_handle, BUFFER * pbuf, int async_flag, int requested_space)
05829 {
05830    int status;
05831    BUFFER_HEADER *pheader = pbuf->buffer_header;
05832    char *pdata = (char *) (pheader + 1);
05833 
05834    /* make sure the buffer never completely full:
05835     * read pointer and write pointer would coincide
05836     * and the code cannot tell if it means the
05837     * buffer is 100% or 100% empty. It will explode
05838     * or lose events */
05839    requested_space += 100;
05840 
05841    if (requested_space >= pheader->size)
05842       return BM_NO_MEMORY;
05843 
05844    while (1) {
05845 
05846       BUFFER_CLIENT *pc;
05847       int n_blocking;
05848       int i;
05849       int size;
05850 
05851       /* check if enough space in buffer */
05852 
05853       size = pheader->read_pointer - pheader->write_pointer;
05854       if (size <= 0)
05855          size += pheader->size;
05856 
05857 #if 0
05858       printf
05859           ("bm_send_event: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d\n",
05860            pheader->read_pointer, pheader->write_pointer, size, pheader->size, requested_space);
05861 #endif
05862 
05863       if (requested_space < size)       /* note the '<' to avoid 100% filling */
05864          return BM_SUCCESS;
05865 
05866       /* if not enough space, find out who's blocking */
05867       n_blocking = 0;
05868 
05869       for (i = 0, pc = pheader->client; i < pheader->max_client_index; i++, pc++)
05870          if (pc->pid) {
05871             if (pc->read_pointer == pheader->read_pointer) {
05872                /*
05873                   First assume that the client with the "minimum" read pointer
05874                   is not really blocking due to a GET_ALL request.
05875                 */
05876                BOOL blocking = FALSE;
05877                int blocking_request_id = 0;
05878                int j;
05879 
05880                /* check if this request blocks */
05881 
05882                EVENT_REQUEST *prequest = pc->event_request;
05883                EVENT_HEADER *pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
05884 
05885                for (j = 0; j < pc->max_request_index; j++, prequest++)
05886                   if (prequest->valid &&
05887                       bm_match_event(prequest->event_id, prequest->trigger_mask, pevent_test)) {
05888                      if (prequest->sampling_type & GET_ALL) {
05889                         blocking = TRUE;
05890                         blocking_request_id = prequest->id;
05891                         break;
05892                      }
05893                   }
05894 
05895                if (blocking) {
05896                   n_blocking++;
05897 
05898                   if (pc->read_wait) {
05899                      char str[80];
05900 #ifdef DEBUG_MSG
05901                      cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
05902 #endif
05903                      sprintf(str, "B %s %d", pheader->name, blocking_request_id);
05904                      ss_resume(pc->port, str);
05905                   }
05906 
05907                } else {
05908                   /*
05909                      The blocking guy has no GET_ALL request for this event
05910                      -> shift its read pointer.
05911                    */
05912 
05913                   int new_read_pointer;
05914                   int increment = sizeof(EVENT_HEADER) +
05915                       ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
05916 
05917                   /* correct increment for DWORD boundary */
05918                   increment = ALIGN8(increment);
05919 
05920                   new_read_pointer = (pc->read_pointer + increment) % pheader->size;
05921 
05922                   if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
05923                      new_read_pointer = 0;
05924 
05925                   pc->read_pointer = new_read_pointer;
05926                }
05927             }
05928          }
05929       /* client loop */
05930       if (n_blocking == 0) {
05931 
05932          BOOL moved;
05933          /*
05934             calculate new global read pointer as "minimum" of
05935             client read pointers
05936           */
05937 
05938          moved = bm_update_read_pointer("bm_send_event", pheader);
05939 
05940          if (!moved) {
05941             cm_msg(MERROR, "bm_wait_for_free_space",
05942                    "BUG: read pointer did not move while waiting for %d bytes, bytes available: %d, buffer size: %d",
05943                    requested_space, size, pheader->size);
05944             return BM_NO_MEMORY;
05945          }
05946 
05947          continue;
05948       }
05949 
05950       /* at least one client is blocking */
05951 
05952       bm_unlock_buffer(buffer_handle);
05953 
05954       /* return now in ASYNC mode */
05955       if (async_flag)
05956          return BM_ASYNC_RETURN;
05957 
05958 #ifdef DEBUG_MSG
05959       cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
05960              pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05961 #endif
05962 
05963       /* signal other clients wait mode */
05964       pheader->client[bm_validate_client_index(pbuf)].write_wait = requested_space;
05965 
05966       status = ss_suspend(1000, MSG_BM);
05967 
05968       /* validate client index: we could have been removed from the buffer */
05969       pheader->client[bm_validate_client_index(pbuf)].write_wait = 0;
05970 
05971       /* return if TCP connection broken */
05972       if (status == SS_ABORT)
05973          return SS_ABORT;
05974 
05975 #ifdef DEBUG_MSG
05976       cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
05977              pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
05978 #endif
05979 
05980       bm_lock_buffer(buffer_handle);
05981    }
05982 }
05983 
05984 /********************************************************************/
05985 /**
05986 Sends an event to a buffer.
05987 This function check if the buffer has enough space for the
05988 event, then copies the event to the buffer in shared memory.
05989 If clients have requests for the event, they are notified via an UDP packet.
05990 \code
05991 char event[1000];
05992 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05993 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05994 
05995 // set first byte of event
05996 *(event+sizeof(EVENT_HEADER)) = <...>
05997 #include <stdio.h>
05998 #include "midas.h"
05999 main()
06000 {
06001  INT status, i;
06002  HNDLE hbuf;
06003  char event[1000];
06004  status = cm_connect_experiment("", "Sample", "Producer", NULL);
06005  if (status != CM_SUCCESS)
06006  return 1;
06007  bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
06008 
06009  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
06010  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
06011 
06012  // set event data
06013  for (i=0 ; i<100 ; i++)
06014  *(event+sizeof(EVENT_HEADER)+i) = i;
06015  // send event
06016  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
06017  cm_disconnect_experiment();
06018  return 0;
06019 }
06020 \endcode
06021 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06022 @param source Address of event buffer
06023 @param buf_size Size of event including event header in bytes
06024 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06025 blocks if the buffer has not enough free space to receive the event.
06026 If TRUE, the function returns immediately with a
06027 value of BM_ASYNC_RETURN without writing the event to the buffer
06028 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
06029 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
06030 buffer has not enough space to receive event<br>
06031 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
06032 One has to increase MAX_EVENT_SIZE in midas.h and
06033 recompile.
06034 */
06035 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
06036 {
06037    EVENT_HEADER *pevent;
06038 
06039    /* check if event size defined in header matches buf_size */
06040    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
06041       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
06042              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER)));
06043       return BM_INVALID_PARAM;
06044    }
06045 
06046    /* check for maximal event size */
06047    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
06048       cm_msg(MERROR, "bm_send_event",
06049              "event size (%d) larger than maximum event size (%d)",
06050              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
06051       return BM_NO_MEMORY;
06052    }
06053 
06054    if (rpc_is_remote())
06055       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
06056 
06057 #ifdef LOCAL_ROUTINES
06058    {
06059       BUFFER *pbuf;
06060       BUFFER_HEADER *pheader;
06061       BUFFER_CLIENT *pclient;
06062       EVENT_REQUEST *prequest;
06063       INT i, j, size, total_size, status;
06064       INT my_client_index;
06065       INT old_write_pointer;
06066       INT num_requests_client;
06067       char *pdata;
06068       INT request_id;
06069 
06070       pbuf = &_buffer[buffer_handle - 1];
06071 
06072       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06073          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06074          return BM_INVALID_HANDLE;
06075       }
06076 
06077       if (!pbuf->attached) {
06078          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06079          return BM_INVALID_HANDLE;
06080       }
06081 
06082       pevent = (EVENT_HEADER *) source;
06083       total_size = buf_size;
06084 
06085       /* round up total_size to next DWORD boundary */
06086       total_size = ALIGN8(total_size);
06087 
06088       /* look if there is space in the cache */
06089       if (pbuf->write_cache_size) {
06090          status = BM_SUCCESS;
06091 
06092          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
06093             status = bm_flush_cache(buffer_handle, async_flag);
06094 
06095          if (status != BM_SUCCESS)
06096             return status;
06097 
06098          if (total_size < pbuf->write_cache_size) {
06099             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
06100 
06101             pbuf->write_cache_wp += total_size;
06102             return BM_SUCCESS;
06103          }
06104       }
06105 
06106       /* we come here only for events that are too big to fit into the cache */
06107 
06108       /* calculate some shorthands */
06109       pheader = pbuf->buffer_header;
06110       pdata = (char *) (pheader + 1);
06111       my_client_index = bm_validate_client_index(pbuf);
06112       pclient = pheader->client;
06113 
06114       /* check if buffer is large enough */
06115       if (total_size >= pheader->size) {
06116          cm_msg(MERROR, "bm_send_event",
06117                 "total event size (%d) larger than buffer size (%d)", total_size, pheader->size);
06118          return BM_NO_MEMORY;
06119       }
06120 
06121       /* lock the buffer */
06122       bm_lock_buffer(buffer_handle);
06123 
06124       status = bm_wait_for_free_space(buffer_handle, pbuf, async_flag, total_size);
06125       if (status != BM_SUCCESS) {
06126          bm_unlock_buffer(buffer_handle);
06127          return status;
06128       }
06129 
06130       /* we have space, so let's copy the event */
06131       old_write_pointer = pheader->write_pointer;
06132 
06133       if (pheader->write_pointer + total_size <= pheader->size) {
06134          memcpy(pdata + pheader->write_pointer, pevent, total_size);
06135          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06136          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06137             pheader->write_pointer = 0;
06138       } else {
06139          /* split event */
06140          size = pheader->size - pheader->write_pointer;
06141 
06142          memcpy(pdata + pheader->write_pointer, pevent, size);
06143          memcpy(pdata, (char *) pevent + size, total_size - size);
06144 
06145          pheader->write_pointer = total_size - size;
06146       }
06147 
06148       /* write pointer was incremented, but there should
06149        * always be some free space in the buffer and the
06150        * write pointer should never cacth up to the read pointer:
06151        * the rest of the code gets confused this happens (buffer 100% full)
06152        * as it is write_pointer == read_pointer can be either
06153        * 100% full or 100% empty. My solution: never fill
06154        * the buffer to 100% */
06155       assert(pheader->write_pointer != pheader->read_pointer);
06156 
06157       /* check which clients have a request for this event */
06158       for (i = 0; i < pheader->max_client_index; i++)
06159          if (pclient[i].pid) {
06160             prequest = pclient[i].event_request;
06161             num_requests_client = 0;
06162             request_id = -1;
06163 
06164             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
06165                if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06166                   if (prequest->sampling_type & GET_ALL)
06167                      pclient[i].num_waiting_events++;
06168 
06169                   num_requests_client++;
06170                   request_id = prequest->id;
06171                }
06172 
06173             /* if that client has a request and is suspended, wake it up */
06174             if (num_requests_client && pclient[i].read_wait) {
06175                char str[80];
06176 #ifdef DEBUG_MSG
06177                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06178 #endif
06179                sprintf(str, "B %s %d", pheader->name, request_id);
06180                ss_resume(pclient[i].port, str);
06181             }
06182 
06183             /* if that client has no request, shift its read pointer */
06184             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
06185                pclient[i].read_pointer = pheader->write_pointer;
06186          }
06187 
06188       /* shift read pointer of own client */
06189       if (pclient[my_client_index].read_pointer == old_write_pointer)
06190          pclient[my_client_index].read_pointer = pheader->write_pointer;
06191 
06192       /* calculate global read pointer as "minimum" of client read pointers */
06193 
06194       bm_update_read_pointer("bm_send_event", pheader);
06195 
06196       /* update statistics */
06197       pheader->num_in_events++;
06198 
06199       /* unlock the buffer */
06200       bm_unlock_buffer(buffer_handle);
06201    }
06202 #endif                          /* LOCAL_ROUTINES */
06203 
06204    return BM_SUCCESS;
06205 }
06206 
06207 /********************************************************************/
06208 /**
06209 Empty write cache.
06210 This function should be used if events in the write cache
06211 should be visible to the consumers immediately. It should be called at the
06212 end of each run, otherwise events could be kept in the write buffer and will
06213 flow to the data of the next run.
06214 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06215 @param async_flag Synchronous/asynchronous flag.
06216 If FALSE, the function blocks if the buffer has not
06217 enough free space to receive the full cache. If TRUE, the function returns
06218 immediately with a value of BM_ASYNC_RETURN without writing the cache.
06219 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
06220 BM_ASYNC_RETURN Routine called with async_flag == TRUE
06221 and buffer has not enough space to receive cache<br>
06222 BM_NO_MEMORY Event is too large for network buffer or event buffer.
06223 One has to increase MAX_EVENT_SIZE in midas.h
06224 and recompile.
06225 */
06226 INT bm_flush_cache(INT buffer_handle, INT async_flag)
06227 {
06228    if (rpc_is_remote())
06229       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
06230 
06231 #ifdef LOCAL_ROUTINES
06232    {
06233       BUFFER *pbuf;
06234       BUFFER_HEADER *pheader;
06235       BUFFER_CLIENT *pclient;
06236       EVENT_HEADER *pevent;
06237       INT i, size, total_size, status;
06238       INT my_client_index;
06239       INT old_write_pointer;
06240       char *pdata;
06241 
06242       pbuf = &_buffer[buffer_handle - 1];
06243 
06244       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06245          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06246          return BM_INVALID_HANDLE;
06247       }
06248 
06249       if (!pbuf->attached) {
06250          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06251          return BM_INVALID_HANDLE;
06252       }
06253 
06254       if (pbuf->write_cache_size == 0)
06255          return BM_SUCCESS;
06256 
06257       /* check if anything needs to be flushed */
06258       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
06259          return BM_SUCCESS;
06260 
06261       /* calculate some shorthands */
06262       pheader = _buffer[buffer_handle - 1].buffer_header;
06263       pdata = (char *) (pheader + 1);
06264       my_client_index = bm_validate_client_index(pbuf);
06265       pclient = pheader->client;
06266       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06267 
06268       /* lock the buffer */
06269       bm_lock_buffer(buffer_handle);
06270 
06271 #ifdef DEBUG_MSG
06272       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06273 #endif
06274 
06275       status = bm_wait_for_free_space(buffer_handle, pbuf, async_flag, pbuf->write_cache_wp);
06276       if (status != BM_SUCCESS) {
06277          bm_unlock_buffer(buffer_handle);
06278          return status;
06279       }
06280 
06281       /* we have space, so let's copy the event */
06282       old_write_pointer = pheader->write_pointer;
06283 
06284 #ifdef DEBUG_MSG
06285       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
06286              pheader->write_pointer);
06287 #endif
06288 
06289       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06290          /* loop over all events in cache */
06291 
06292          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06293          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06294 
06295          assert(total_size > 0);
06296          assert(total_size <= pheader->size);
06297 
06298          /* correct size for DWORD boundary */
06299          total_size = ALIGN8(total_size);
06300 
06301          if (pheader->write_pointer + total_size <= pheader->size) {
06302             memcpy(pdata + pheader->write_pointer, pevent, total_size);
06303             pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06304             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06305                pheader->write_pointer = 0;
06306          } else {
06307             /* split event */
06308             size = pheader->size - pheader->write_pointer;
06309 
06310             memcpy(pdata + pheader->write_pointer, pevent, size);
06311             memcpy(pdata, (char *) pevent + size, total_size - size);
06312 
06313             pheader->write_pointer = total_size - size;
06314          }
06315 
06316          /* see comment for the same code in bm_send_event().
06317           * We make sure the buffer is nevere 100% full */
06318          assert(pheader->write_pointer != pheader->read_pointer);
06319 
06320          /* this loop does not loop forever because write_cache_rp
06321           * is monotonously incremented here. write_cache_wp does
06322           * not change */
06323 
06324          pbuf->write_cache_rp += total_size;
06325       }
06326 
06327       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06328 
06329       /* check which clients are waiting */
06330       for (i = 0; i < pheader->max_client_index; i++)
06331          if (pclient[i].pid && pclient[i].read_wait) {
06332             char str[80];
06333 #ifdef DEBUG_MSG
06334             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06335 #endif
06336             sprintf(str, "B %s %d", pheader->name, -1);
06337             ss_resume(pclient[i].port, str);
06338          }
06339 
06340       /* shift read pointer of own client */
06341       if (pclient[my_client_index].read_pointer == old_write_pointer)
06342          pclient[my_client_index].read_pointer = pheader->write_pointer;
06343 
06344       /* calculate global read pointer as "minimum" of client read pointers */
06345 
06346       bm_update_read_pointer("bm_flush_cache", pheader);
06347 
06348       /* update statistics */
06349       pheader->num_in_events++;
06350 
06351       /* unlock the buffer */
06352       bm_unlock_buffer(buffer_handle);
06353    }
06354 #endif                          /* LOCAL_ROUTINES */
06355 
06356    return BM_SUCCESS;
06357 }
06358 
06359 /********************************************************************/
06360 /**
06361 Receives events directly.
06362 This function is an alternative way to receive events without
06363 a main loop.
06364 
06365 It can be used in analysis systems which actively receive events,
06366 rather than using callbacks. A analysis package could for example contain its own
06367 command line interface. A command
06368 like "receive 1000 events" could make it necessary to call bm_receive_event()
06369 1000 times in a row to receive these events and then return back to the
06370 command line prompt.
06371 The according bm_request_event() call contains NULL as the
06372 callback routine to indicate that bm_receive_event() is called to receive
06373 events.
06374 \code
06375 #include <stdio.h>
06376 #include "midas.h"
06377 void process_event(EVENT_HEADER *pheader)
06378 {
06379  printf("Received event #%d\r",
06380  pheader->serial_number);
06381 }
06382 main()
06383 {
06384   INT status, request_id;
06385   HNDLE hbuf;
06386   char event_buffer[1000];
06387   status = cm_connect_experiment("", "Sample",
06388   "Simple Analyzer", NULL);
06389   if (status != CM_SUCCESS)
06390    return 1;
06391   bm_open_buffer(EVENT_BUFFER_NAME, 2*MAX_EVENT_SIZE, &hbuf);
06392   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06393 
06394   do
06395   {
06396    size = sizeof(event_buffer);
06397    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06398   if (status == CM_SUCCESS)
06399    process_event((EVENT_HEADER *) event_buffer);
06400    <...do something else...>
06401    status = cm_yield(0);
06402   } while (status != RPC_SHUTDOWN &&
06403   status != SS_ABORT);
06404   cm_disconnect_experiment();
06405   return 0;
06406 }
06407 \endcode
06408 @param buffer_handle buffer handle
06409 @param destination destination address where event is written to
06410 @param buf_size size of destination buffer on input, size of event plus
06411 header on return.
06412 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06413 blocks if no event is available. If TRUE, the function returns immediately
06414 with a value of BM_ASYNC_RETURN without receiving any event.
06415 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06416 BM_TRUNCATED   The event is larger than the destination buffer and was
06417                therefore truncated <br>
06418 BM_ASYNC_RETURN No event available
06419 */
06420 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06421 {
06422    if (rpc_is_remote()) {
06423       int status, old_timeout = 0;
06424 
06425       if (*buf_size > (INT) NET_BUFFER_SIZE) {
06426          cm_msg(MERROR, "bm_receive_event", "max. event size larger than NET_BUFFER_SIZE");
06427          return RPC_NET_ERROR;
06428       }
06429 
06430       if (!async_flag) {
06431          old_timeout = rpc_get_option(-1, RPC_OTIMEOUT);
06432          rpc_set_option(-1, RPC_OTIMEOUT, 0);
06433       }
06434 
06435       status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, destination, buf_size, async_flag);
06436 
06437       if (!async_flag) {
06438          rpc_set_option(-1, RPC_OTIMEOUT, old_timeout);
06439       }
06440 
06441       return status;
06442    }
06443 #ifdef LOCAL_ROUTINES
06444    {
06445       BUFFER *pbuf;
06446       BUFFER_HEADER *pheader;
06447       BUFFER_CLIENT *pclient, *pc;
06448       char *pdata;
06449       INT convert_flags;
06450       INT i, size, max_size;
06451       INT status = 0;
06452       INT my_client_index;
06453       BOOL cache_is_full = FALSE;
06454       BOOL use_event_buffer = FALSE;
06455       int cycle = 0;
06456 
06457       pbuf = &_buffer[buffer_handle - 1];
06458 
06459       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06460          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06461          return BM_INVALID_HANDLE;
06462       }
06463 
06464       if (!pbuf->attached) {
06465          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06466          return BM_INVALID_HANDLE;
06467       }
06468 
06469       max_size = *buf_size;
06470       *buf_size = 0;
06471 
06472       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06473          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06474       else
06475          convert_flags = 0;
06476 
06477       /* look if there is anything in the cache */
06478       if (bm_read_cache_has_events(pbuf))
06479          return bm_copy_from_cache(pbuf, destination, max_size, buf_size, convert_flags);
06480 
06481     LOOP:
06482       /* make sure we do bm_validate_client_index() after sleeping */
06483 
06484       /* calculate some shorthands */
06485       pheader = pbuf->buffer_header;
06486       pdata = (char *) (pheader + 1);
06487       my_client_index = bm_validate_client_index(pbuf);
06488       pclient = pheader->client;
06489       pc = pheader->client + my_client_index;
06490 
06491       /* first do a quick check without locking the buffer */
06492       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06493          return BM_ASYNC_RETURN;
06494 
06495       /* lock the buffer */
06496       bm_lock_buffer(buffer_handle);
06497 
06498       while (pheader->write_pointer == pc->read_pointer) {
06499 
06500          bm_unlock_buffer(buffer_handle);
06501 
06502          /* return now in ASYNC mode */
06503          if (async_flag == ASYNC)
06504             return BM_ASYNC_RETURN;
06505 
06506          pc->read_wait = TRUE;
06507 
06508          /* check again pointers (may have moved in between) */
06509          if (pheader->write_pointer == pc->read_pointer) {
06510 #ifdef DEBUG_MSG
06511             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06512                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06513 #endif
06514 
06515             status = ss_suspend(1000, MSG_BM);
06516 
06517 #ifdef DEBUG_MSG
06518             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d", pheader->read_pointer, pheader->write_pointer);
06519 #endif
06520 
06521             /* return if TCP connection broken */
06522             if (status == SS_ABORT)
06523                return SS_ABORT;
06524          }
06525 
06526          pc->read_wait = FALSE;
06527 
06528          /* validate client_index: somebody may have disconnected us from the buffer */
06529          bm_validate_client_index(pbuf);
06530 
06531          bm_lock_buffer(buffer_handle);
06532       }
06533 
06534       /* check if event at current read pointer matches a request */
06535 
06536       do {
06537          int new_read_pointer;
06538          int total_size;        /* size of the event */
06539          EVENT_REQUEST *prequest;
06540          EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06541 
06542          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06543          total_size = ALIGN8(total_size);
06544 
06545          assert(total_size > 0);
06546          assert(total_size <= pheader->size);
06547 
06548          prequest = pc->event_request;
06549 
06550          /* loop over all requests: if this event matches a request,
06551           * copy it to the read cache */
06552 
06553          for (i = 0; i < pc->max_request_index; i++, prequest++)
06554             if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06555 
06556                /* we found a request for this event, so copy it */
06557 
06558                if (pbuf->read_cache_size > 0 && total_size < pbuf->read_cache_size) {
06559 
06560                   /* copy event to cache, if there is room */
06561 
06562                   if (pbuf->read_cache_wp + total_size >= pbuf->read_cache_size) {
06563                      cache_is_full = TRUE;
06564                      break;     /* exit loop over requests */
06565                   }
06566 
06567                   if (pc->read_pointer + total_size <= pheader->size) {
06568                      /* copy event to cache */
06569                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06570                   } else {
06571                      /* event is splitted */
06572                      size = pheader->size - pc->read_pointer;
06573                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06574                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size, pdata, total_size - size);
06575                   }
06576 
06577                   pbuf->read_cache_wp += total_size;
06578 
06579                } else {
06580                   int copy_size = total_size;
06581 
06582                   /* if there are events in the read cache,
06583                    * we should dispatch them before we
06584                    * despatch this oversize event */
06585 
06586                   if (bm_read_cache_has_events(pbuf)) {
06587                      cache_is_full = TRUE;
06588                      break;     /* exit loop over requests */
06589                   }
06590 
06591                   use_event_buffer = TRUE;
06592 
06593                   status = BM_SUCCESS;
06594                   if (copy_size > max_size) {
06595                      copy_size = max_size;
06596                      cm_msg(MERROR, "bm_receive_event",
06597                             "event size %d larger than buffer size %d", total_size, max_size);
06598                      status = BM_TRUNCATED;
06599                   }
06600 
06601                   if (pc->read_pointer + total_size <= pheader->size) {
06602                      /* event is not splitted */
06603                      memcpy(destination, pevent, copy_size);
06604                   } else {
06605                      /* event is splitted */
06606                      size = pheader->size - pc->read_pointer;
06607 
06608                      if (size > max_size)
06609                         memcpy(destination, pevent, max_size);
06610                      else
06611                         memcpy(destination, pevent, size);
06612 
06613                      if (total_size > max_size) {
06614                         if (size <= max_size)
06615                            memcpy((char *) destination + size, pdata, max_size - size);
06616                      } else
06617                         memcpy((char *) destination + size, pdata, total_size - size);
06618                   }
06619 
06620                   *buf_size = copy_size;
06621 
06622                   bm_convert_event_header((EVENT_HEADER *) destination, convert_flags);
06623                }
06624 
06625                /* update statistics */
06626                pheader->num_out_events++;
06627                break;           /* stop looping over requests */
06628             }
06629 
06630          if (cache_is_full)
06631             break;              /* exit from loop over events in data buffer, leaving the current event untouched */
06632 
06633          /* shift read pointer */
06634 
06635          new_read_pointer = pc->read_pointer + total_size;
06636          if (new_read_pointer >= pheader->size) {
06637             new_read_pointer = new_read_pointer % pheader->size;
06638 
06639             /* make sure we loop over the data buffer no more than once */
06640             cycle++;
06641             assert(cycle < 2);
06642          }
06643 
06644          /* make sure we do not split the event header at the end of the buffer */
06645          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06646             new_read_pointer = 0;
06647 
06648 #ifdef DEBUG_MSG
06649          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06650                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found, total_size);
06651 #endif
06652 
06653          pc->read_pointer = new_read_pointer;
06654 
06655          if (use_event_buffer)
06656             break;              /* exit from loop over events in data buffer */
06657 
06658       } while (pheader->write_pointer != pc->read_pointer);
06659 
06660       /* calculate global read pointer as "minimum" of client read pointers */
06661 
06662       bm_update_read_pointer("bm_receive_event", pheader);
06663 
06664       /*
06665          If read pointer has been changed, it may have freed up some space
06666          for waiting producers. So check if free space is now more than 50%
06667          of the buffer size and wake waiting producers.
06668        */
06669 
06670       bm_wakeup_producers(pheader, pc);
06671 
06672       bm_unlock_buffer(buffer_handle);
06673 
06674       if (bm_read_cache_has_events(pbuf)) {
06675          assert(!use_event_buffer);     /* events only go into the _event_buffer when read cache is empty */
06676          return bm_copy_from_cache(pbuf, destination, max_size, buf_size, convert_flags);
06677       }
06678 
06679       if (use_event_buffer)
06680          return status;
06681 
06682       goto LOOP;
06683    }
06684 #else                           /* LOCAL_ROUTINES */
06685 
06686    return SS_SUCCESS;
06687 #endif
06688 }
06689 
06690 /********************************************************************/
06691 /**
06692 Skip all events in current buffer.
06693 
06694 Useful for single event displays to see the newest events
06695 @param buffer_handle      Handle of the buffer. Must be obtained
06696                           via bm_open_buffer.
06697 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR
06698 */
06699 INT bm_skip_event(INT buffer_handle)
06700 {
06701    if (rpc_is_remote())
06702       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
06703 
06704 #ifdef LOCAL_ROUTINES
06705    {
06706       BUFFER *pbuf;
06707       BUFFER_HEADER *pheader;
06708       BUFFER_CLIENT *pclient;
06709 
06710       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06711          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06712          return BM_INVALID_HANDLE;
06713       }
06714 
06715       pbuf = &_buffer[buffer_handle - 1];
06716       pheader = pbuf->buffer_header;
06717 
06718       if (!pbuf->attached) {
06719          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
06720          return BM_INVALID_HANDLE;
06721       }
06722 
06723       /* clear cache */
06724       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
06725          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06726 
06727       bm_lock_buffer(buffer_handle);
06728 
06729       /* forward read pointer to global write pointer */
06730       pclient = pheader->client + bm_validate_client_index(pbuf);
06731       pclient->read_pointer = pheader->write_pointer;
06732 
06733       bm_unlock_buffer(buffer_handle);
06734    }
06735 #endif
06736 
06737    return BM_SUCCESS;
06738 }
06739 
06740 /********************************************************************/
06741 /**
06742 Check a buffer if an event is available and call the dispatch function if found.
06743 @param buffer_name       Name of buffer
06744 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN,
06745                     RPC_NET_ERROR
06746 */
06747 INT bm_push_event(char *buffer_name)
06748 {
06749 #ifdef LOCAL_ROUTINES
06750    {
06751       BUFFER *pbuf;
06752       BUFFER_HEADER *pheader;
06753       BUFFER_CLIENT *pclient, *pc;
06754       char *pdata;
06755       INT i, size, buffer_handle;
06756       INT my_client_index;
06757       BOOL use_event_buffer = 0;
06758       BOOL cache_is_full = 0;
06759       int cycle = 0;
06760 
06761       for (i = 0; i < _buffer_entries; i++)
06762          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
06763             break;
06764       if (i == _buffer_entries)
06765          return BM_INVALID_HANDLE;
06766 
06767       buffer_handle = i + 1;
06768       pbuf = &_buffer[buffer_handle - 1];
06769 
06770       if (!pbuf->attached)
06771          return BM_INVALID_HANDLE;
06772 
06773       /* return immediately if no callback routine is defined */
06774       if (!pbuf->callback)
06775          return BM_SUCCESS;
06776 
06777       if (_event_buffer_size == 0) {
06778          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
06779          if (_event_buffer == NULL) {
06780             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
06781             return BM_NO_MEMORY;
06782          }
06783          _event_buffer_size = 1000;
06784       }
06785 
06786       /* look if there is anything in the cache */
06787       if (bm_read_cache_has_events(pbuf)) {
06788          bm_dispatch_from_cache(pbuf, buffer_handle);
06789          return BM_MORE_EVENTS;
06790       }
06791 
06792       /* calculate some shorthands */
06793       pheader = pbuf->buffer_header;
06794       pdata = (char *) (pheader + 1);
06795       my_client_index = bm_validate_client_index(pbuf);
06796       pclient = pheader->client;
06797       pc = pheader->client + my_client_index;
06798 
06799       /* first do a quick check without locking the buffer */
06800       if (pheader->write_pointer == pc->read_pointer)
06801          return BM_SUCCESS;
06802 
06803       /* lock the buffer */
06804       bm_lock_buffer(buffer_handle);
06805 
06806       if (pheader->write_pointer == pc->read_pointer) {
06807 
06808          bm_unlock_buffer(buffer_handle);
06809 
06810          /* return if no event available */
06811          return BM_SUCCESS;
06812       }
06813 
06814       /* loop over all events in the buffer */
06815 
06816       do {
06817          int new_read_pointer;
06818          int total_size;        /* size of the event */
06819          EVENT_REQUEST *prequest;
06820          EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06821 
06822          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06823          total_size = ALIGN8(total_size);
06824 
06825          assert(total_size > 0);
06826          assert(total_size <= pheader->size);
06827 
06828          prequest = pc->event_request;
06829 
06830          /* loop over all requests: if this event matches a request,
06831           * copy it to the read cache */
06832 
06833          for (i = 0; i < pc->max_request_index; i++, prequest++)
06834             if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06835 
06836                /* we found a request for this event, so copy it */
06837 
06838                if (pbuf->read_cache_size > 0 && total_size < pbuf->read_cache_size) {
06839 
06840                   /* copy event to cache, if there is room */
06841 
06842                   if (pbuf->read_cache_wp + total_size >= pbuf->read_cache_size) {
06843                      cache_is_full = TRUE;
06844                      break;     /* exit loop over requests */
06845                   }
06846 
06847                   if (pc->read_pointer + total_size <= pheader->size) {
06848                      /* copy event to cache */
06849                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06850                   } else {
06851                      /* event is splitted */
06852                      size = pheader->size - pc->read_pointer;
06853                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06854                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size, pdata, total_size - size);
06855                   }
06856 
06857                   pbuf->read_cache_wp += total_size;
06858 
06859                } else {
06860                   /* copy event to copy buffer */
06861 
06862                   /* if there are events in the read cache,
06863                    * we should dispatch them before we
06864                    * despatch this oversize event */
06865 
06866                   if (bm_read_cache_has_events(pbuf)) {
06867                      cache_is_full = TRUE;
06868                      break;     /* exit loop over requests */
06869                   }
06870 
06871                   use_event_buffer = TRUE;
06872 
06873                   if (total_size > _event_buffer_size) {
06874                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
06875                      _event_buffer_size = total_size;
06876                   }
06877 
06878                   if (pc->read_pointer + total_size <= pheader->size) {
06879                      memcpy(_event_buffer, pevent, total_size);
06880                   } else {
06881                      /* event is splitted */
06882                      size = pheader->size - pc->read_pointer;
06883 
06884                      memcpy(_event_buffer, pevent, size);
06885                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
06886                   }
06887                }
06888 
06889                /* update statistics */
06890                pheader->num_out_events++;
06891                break;           /* exit loop over event requests */
06892 
06893             }
06894          /* end of loop over event requests */
06895          if (cache_is_full)
06896             break;              /* exit from loop over events in data buffer, leaving the current event untouched */
06897 
06898          /* shift read pointer */
06899 
06900          new_read_pointer = pc->read_pointer + total_size;
06901          if (new_read_pointer >= pheader->size) {
06902             new_read_pointer = new_read_pointer % pheader->size;
06903 
06904             /* make sure we loop over the data buffer no more than once */
06905             cycle++;
06906             assert(cycle < 2);
06907          }
06908 
06909          /* make sure we do not split the event header at the end of the buffer */
06910          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06911             new_read_pointer = 0;
06912 
06913 #ifdef DEBUG_MSG
06914          cm_msg(MDEBUG, "bm_push_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06915                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found, total_size);
06916 #endif
06917 
06918          pc->read_pointer = new_read_pointer;
06919 
06920          if (use_event_buffer)
06921             break;              /* exit from loop over events in data buffer */
06922 
06923       } while (pheader->write_pointer != pc->read_pointer);
06924 
06925       /* calculate global read pointer as "minimum" of client read pointers */
06926 
06927       bm_update_read_pointer("bm_push_event", pheader);
06928 
06929       /*
06930          If read pointer has been changed, it may have freed up some space
06931          for waiting producers. So check if free space is now more than 50%
06932          of the buffer size and wake waiting producers.
06933        */
06934 
06935       bm_wakeup_producers(pheader, pc);
06936 
06937       bm_unlock_buffer(buffer_handle);
06938 
06939       if (bm_read_cache_has_events(pbuf)) {
06940          assert(!use_event_buffer);     /* events only go into the _event_buffer when read cache is empty */
06941          bm_dispatch_from_cache(pbuf, buffer_handle);
06942          return BM_MORE_EVENTS;
06943       }
06944 
06945       if (use_event_buffer) {
06946          bm_dispatch_event(buffer_handle, _event_buffer);
06947          return BM_MORE_EVENTS;
06948       }
06949 
06950       return BM_SUCCESS;
06951    }
06952 #else                           /* LOCAL_ROUTINES */
06953 
06954    return BM_SUCCESS;
06955 #endif
06956 }
06957 
06958 /********************************************************************/
06959 /**
06960 Check if any requested event is waiting in a buffer
06961 @return TRUE             More events are waiting<br>
06962         FALSE            No more events are waiting
06963 */
06964 INT bm_check_buffers()
06965 {
06966 #ifdef LOCAL_ROUTINES
06967    {
06968       INT idx, status = 0;
06969       INT server_type, server_conn, tid;
06970       BOOL bMore;
06971       DWORD start_time;
06972 
06973       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
06974       server_conn = rpc_get_server_acception();
06975       tid = ss_gettid();
06976 
06977       /* if running as a server, buffer checking is done by client
06978          via ASYNC bm_receive_event */
06979       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
06980          return FALSE;
06981 
06982       bMore = FALSE;
06983       start_time = ss_millitime();
06984 
06985       /* go through all buffers */
06986       for (idx = 0; idx < _buffer_entries; idx++) {
06987          if (server_type == ST_SINGLE && _buffer[idx].index != server_conn)
06988             continue;
06989 
06990          if (server_type != ST_SINGLE && _buffer[idx].index != tid)
06991             continue;
06992 
06993          if (!_buffer[idx].attached)
06994             continue;
06995 
06996          do {
06997 
06998             /* one bm_push_event could cause a run stop and a buffer close, which
06999                would crash the next call to bm_push_event(). So check for valid
07000                buffer on each call */
07001             if (idx < _buffer_entries && _buffer[idx].buffer_header->name != NULL)
07002                status = bm_push_event(_buffer[idx].buffer_header->name);
07003 
07004             if (status != BM_MORE_EVENTS)
07005                break;
07006 
07007             /* stop after one second */
07008             if (ss_millitime() - start_time > 1000) {
07009                bMore = TRUE;
07010                break;
07011             }
07012 
07013          } while (TRUE);
07014       }
07015 
07016       return bMore;
07017 
07018    }
07019 #else                           /* LOCAL_ROUTINES */
07020 
07021    return FALSE;
07022 
07023 #endif
07024 }
07025 
07026 /**dox***************************************************************/
07027 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07028 
07029 /********************************************************************/
07030 INT bm_mark_read_waiting(BOOL flag)
07031 /********************************************************************\
07032 
07033   Routine: bm_mark_read_waiting
07034 
07035   Purpose: Mark all open buffers ready for receiving events.
07036            Called internally by ss_suspend
07037 
07038 
07039   Input:
07040     BOOL flag               TRUE for waiting, FALSE for not waiting
07041 
07042   Output:
07043     none
07044 
07045   Function value:
07046     BM_SUCCESS              Successful completion
07047 
07048 \********************************************************************/
07049 {
07050    if (rpc_is_remote())
07051       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
07052 
07053 #ifdef LOCAL_ROUTINES
07054    {
07055       INT i;
07056       BUFFER_HEADER *pheader;
07057       BUFFER_CLIENT *pclient;
07058 
07059       /* Mark all buffer for read waiting */
07060       for (i = 0; i < _buffer_entries; i++) {
07061          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
07062              _buffer[i].index != rpc_get_server_acception())
07063             continue;
07064 
07065          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE && _buffer[i].index != ss_gettid())
07066             continue;
07067 
07068          if (!_buffer[i].attached)
07069             continue;
07070 
07071          pheader = _buffer[i].buffer_header;
07072          pclient = pheader->client + bm_validate_client_index(&_buffer[i]);
07073          pclient->read_wait = flag;
07074       }
07075    }
07076 #endif                          /* LOCAL_ROUTINES */
07077 
07078    return BM_SUCCESS;
07079 }
07080 
07081 /********************************************************************/
07082 INT bm_notify_client(char *buffer_name, int s)
07083 /********************************************************************\
07084 
07085   Routine: bm_notify_client
07086 
07087   Purpose: Called by cm_dispatch_ipc. Send an event notification to
07088            the connected client
07089 
07090   Input:
07091     char  *buffer_name      Name of buffer
07092     int   s                 Network socket to client
07093 
07094   Output:
07095     none
07096 
07097   Function value:
07098     BM_SUCCESS              Successful completion
07099 
07100 \********************************************************************/
07101 {
07102    char buffer[32];
07103    NET_COMMAND *nc;
07104    INT i, convert_flags;
07105    static DWORD last_time = 0;
07106 
07107    for (i = 0; i < _buffer_entries; i++)
07108       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07109          break;
07110    if (i == _buffer_entries)
07111       return BM_INVALID_HANDLE;
07112 
07113    /* don't send notification if client has no callback defined
07114       to receive events -> client calls bm_receive_event manually */
07115    if (!_buffer[i].callback)
07116       return DB_SUCCESS;
07117 
07118    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
07119 
07120    /* only send notification once each 500ms */
07121    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
07122       return DB_SUCCESS;
07123 
07124    last_time = ss_millitime();
07125 
07126    if (convert_flags & CF_ASCII) {
07127       sprintf(buffer, "MSG_BM&%s", buffer_name);
07128       send_tcp(s, buffer, strlen(buffer) + 1, 0);
07129    } else {
07130       nc = (NET_COMMAND *) buffer;
07131 
07132       nc->header.routine_id = MSG_BM;
07133       nc->header.param_size = 0;
07134 
07135       if (convert_flags) {
07136          rpc_convert_single(&nc->header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
07137          rpc_convert_single(&nc->header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
07138       }
07139 
07140       /* send the update notification to the client */
07141       send_tcp(s, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
07142    }
07143 
07144    return BM_SUCCESS;
07145 }
07146 
07147 /********************************************************************/
07148 INT bm_poll_event(INT flag)
07149 /********************************************************************\
07150 
07151   Routine: bm_poll_event
07152 
07153   Purpose: Poll an event from a remote server. Gets called by
07154            rpc_client_dispatch
07155 
07156   Input:
07157     INT flag         TRUE if called from cm_yield
07158 
07159   Output:
07160     none
07161 
07162   Function value:
07163     TRUE             More events are waiting
07164     FALSE            No more events are waiting
07165     SS_ABORT         Network connection broken
07166 
07167 \********************************************************************/
07168 {
07169    INT status, size, i, request_id;
07170    DWORD start_time;
07171    BOOL bMore;
07172    static BOOL bMoreLast = FALSE;
07173 
07174    if (_event_buffer_size == 0) {
07175       _event_buffer = (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
07176       if (!_event_buffer) {
07177          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer");
07178          return SS_ABORT;
07179       }
07180       _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
07181    }
07182 
07183    start_time = ss_millitime();
07184 
07185    /* if we got event notification, turn off read_wait */
07186    if (!flag)
07187       bm_mark_read_waiting(FALSE);
07188 
07189    /* if called from yield, return if no more events */
07190    if (flag) {
07191       if (!bMoreLast)
07192          return FALSE;
07193    }
07194 
07195    bMore = FALSE;
07196 
07197    /* loop over all requests */
07198    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07199       /* continue if no dispatcher set (manual bm_receive_event) */
07200       if (_request_list[request_id].dispatcher == NULL)
07201          continue;
07202 
07203       do {
07204          /* receive event */
07205          size = _event_buffer_size;
07206          status = bm_receive_event(_request_list[request_id].buffer_handle, _event_buffer, &size, ASYNC);
07207 
07208          /* call user function if successful */
07209          if (status == BM_SUCCESS)
07210             /* search proper request for this event */
07211             for (i = 0; i < _request_list_entries; i++)
07212                if ((_request_list[i].buffer_handle ==
07213                     _request_list[request_id].buffer_handle) &&
07214                    bm_match_event(_request_list[i].event_id, _request_list[i].trigger_mask, _event_buffer)) {
07215                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07216                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07217                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07218                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07219                                          _request_list[i].dispatcher);
07220                   else
07221                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07222                                                  _event_buffer,
07223                                                  (void *) (((EVENT_HEADER *) _event_buffer) + 1));
07224                }
07225 
07226          /* break if no more events */
07227          if (status == BM_ASYNC_RETURN)
07228             break;
07229 
07230          /* break if server died */
07231          if (status == RPC_NET_ERROR)
07232             return SS_ABORT;
07233 
07234          /* stop after one second */
07235          if (ss_millitime() - start_time > 1000) {
07236             bMore = TRUE;
07237             break;
07238          }
07239 
07240       } while (TRUE);
07241    }
07242 
07243    if (!bMore)
07244       bm_mark_read_waiting(TRUE);
07245 
07246    bMoreLast = bMore;
07247 
07248    return bMore;
07249 }
07250 
07251 /**dox***************************************************************/
07252 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07253 
07254 /********************************************************************/
07255 /**
07256 Clears event buffer and cache.
07257 If an event buffer is large and a consumer is slow in analyzing
07258 events, events are usually received some time after they are produced.
07259 This effect is even more experienced if a read cache is used
07260 (via bm_set_cache_size()).
07261 When changes to the hardware are made in the experience, the consumer will then
07262 still analyze old events before any new event which reflects the hardware change.
07263 Users can be fooled by looking at histograms which reflect the hardware change
07264 many seconds after they have been made.
07265 
07266 To overcome this potential problem, the analyzer can call
07267 bm_empty_buffers() just after the hardware change has been made which
07268 skips all old events contained in event buffers and read caches.
07269 Technically this is done by forwarding the read pointer of the client.
07270 No events are really deleted, they are still visible to other clients like
07271 the logger.
07272 
07273 Note that the front-end also contains write buffers which can delay the
07274 delivery of events.
07275 The standard front-end framework mfe.c reduces this effect by flushing
07276 all buffers once every second.
07277 @return BM_SUCCESS
07278 */
07279 INT bm_empty_buffers()
07280 {
07281    if (rpc_is_remote())
07282       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07283 
07284 #ifdef LOCAL_ROUTINES
07285    {
07286       INT idx, server_type, server_conn, tid;
07287       BUFFER *pbuf;
07288       BUFFER_CLIENT *pclient;
07289 
07290       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07291       server_conn = rpc_get_server_acception();
07292       tid = ss_gettid();
07293 
07294       /* go through all buffers */
07295       for (idx = 0; idx < _buffer_entries; idx++) {
07296          if (server_type == ST_SINGLE && _buffer[idx].index != server_conn)
07297             continue;
07298 
07299          if (server_type != ST_SINGLE && _buffer[idx].index != tid)
07300             continue;
07301 
07302          if (!_buffer[idx].attached)
07303             continue;
07304 
07305          pbuf = &_buffer[idx];
07306 
07307          /* empty cache */
07308          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07309 
07310          /* set read pointer to write pointer */
07311          pclient = (pbuf->buffer_header)->client + bm_validate_client_index(pbuf);
07312          bm_lock_buffer(idx + 1);
07313          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07314          bm_unlock_buffer(idx + 1);
07315       }
07316 
07317    }
07318 #endif                          /* LOCAL_ROUTINES */
07319 
07320    return BM_SUCCESS;
07321 }
07322 
07323 /**dox***************************************************************/
07324 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07325 
07326 #define MAX_DEFRAG_EVENTS 10
07327 
07328 typedef struct {
07329    WORD event_id;
07330    DWORD data_size;
07331    DWORD received;
07332    EVENT_HEADER *pevent;
07333 } EVENT_DEFRAG_BUFFER;
07334 
07335 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07336 
07337 /********************************************************************/
07338 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07339                          EVENT_HEADER * pevent, void *pdata,
07340                          void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *, void *))
07341 /********************************************************************\
07342 
07343   Routine: bm_defragment_event
07344 
07345   Purpose: Called internally from the event receiving routines
07346            bm_push_event and bm_poll_event to recombine event
07347            fragments and call the user callback routine upon
07348            completion.
07349 
07350   Input:
07351     HNDLE buffer_handle  Handle for the buffer containing event
07352     HNDLE request_id     Handle for event request
07353     EVENT_HEADER *pevent Pointer to event header
07354     void *pata           Pointer to event data
07355     dispatcher()         User callback routine
07356 
07357   Output:
07358     <calls dispatcher() after successfull recombination of event>
07359 
07360   Function value:
07361     void
07362 
07363 \********************************************************************/
07364 {
07365    INT i;
07366    static int j = -1;
07367 
07368    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07369       /*---- start new event ----*/
07370 
07371       //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
07372 
07373       /* check if fragments already stored */
07374       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07375          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07376             break;
07377 
07378       if (i < MAX_DEFRAG_EVENTS) {
07379          free(defrag_buffer[i].pevent);
07380          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07381          cm_msg(MERROR, "bm_defragement_event",
07382                 "Received new event with ID %d while old fragments were not completed",
07383                 (pevent->event_id & 0x0FFF));
07384       }
07385 
07386       /* search new slot */
07387       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07388          if (defrag_buffer[i].event_id == 0)
07389             break;
07390 
07391       if (i == MAX_DEFRAG_EVENTS) {
07392          cm_msg(MERROR, "bm_defragment_event",
07393                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07394          return;
07395       }
07396 
07397       /* check event size */
07398       if (pevent->data_size != sizeof(DWORD)) {
07399          cm_msg(MERROR, "bm_defragment_event",
07400                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07401                 pevent->data_size, sizeof(DWORD));
07402          return;
07403       }
07404 
07405       /* setup defragment buffer */
07406       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07407       defrag_buffer[i].data_size = *(DWORD *) pdata;
07408       defrag_buffer[i].received = 0;
07409       defrag_buffer[i].pevent = (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07410 
07411       if (defrag_buffer[i].pevent == NULL) {
07412          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07413          cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
07414          return;
07415       }
07416 
07417       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07418       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07419       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07420 
07421       // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
07422       //       pevent->serial_number, defrag_buffer[i].data_size);
07423 
07424       j = 0;
07425 
07426       return;
07427    }
07428 
07429    /* search buffer for that event */
07430    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07431       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07432          break;
07433 
07434    if (i == MAX_DEFRAG_EVENTS) {
07435       /* no buffer available -> no first fragment received */
07436       cm_msg(MERROR, "bm_defragement_event",
07437              "Received fragment without first fragment (ID %d) Ser#:%d",
07438              pevent->event_id & 0x0FFF, pevent->serial_number);
07439       return;
07440    }
07441 
07442    /* add fragment to buffer */
07443    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
07444       free(defrag_buffer[i].pevent);
07445       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07446       cm_msg(MERROR, "bm_defragement_event",
07447              "Received fragments with more data (%d) than event size (%d)",
07448              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
07449       return;
07450    }
07451 
07452    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07453           defrag_buffer[i].received, pdata, pevent->data_size);
07454 
07455    defrag_buffer[i].received += pevent->data_size;
07456 
07457    //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
07458    //       defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
07459 
07460    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
07461       /* event complete */
07462       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
07463       free(defrag_buffer[i].pevent);
07464       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07465    }
07466 }
07467 
07468 /**dox***************************************************************/
07469 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07470 
07471 /**dox***************************************************************/
07472                                                                                                              /** @} *//* end of bmfunctionc */
07473 
07474 /**dox***************************************************************/
07475 /** @addtogroup rpcfunctionc
07476  *
07477  *  @{  */
07478 
07479 /**dox***************************************************************/
07480 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07481 
07482 /********************************************************************\
07483 *                                                                    *
07484 *                         RPC functions                              *
07485 *                                                                    *
07486 \********************************************************************/
07487 
07488 /* globals */
07489 
07490 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
07491 RPC_SERVER_CONNECTION _server_connection;
07492 
07493 static int _lsock;
07494 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
07495 static INT _server_acception_index = 0;
07496 static INT _server_type;
07497 static char _server_name[256];
07498 
07499 static RPC_LIST *rpc_list = NULL;
07500 
07501 int _opt_tcp_size = OPT_TCP_SIZE;
07502 
07503 
07504 /********************************************************************\
07505 *                       conversion functions                         *
07506 \********************************************************************/
07507 
07508 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
07509 {
07510    *convert_flags = 0;
07511 
07512    /* big/little endian conversion */
07513    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
07514         (hw_type & DRI_LITTLE_ENDIAN)) ||
07515        ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
07516       *convert_flags |= CF_ENDIAN;
07517 
07518    /* float conversion between IEEE and VAX G */
07519    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
07520       *convert_flags |= CF_VAX2IEEE;
07521 
07522    /* float conversion between VAX G and IEEE */
07523    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
07524       *convert_flags |= CF_IEEE2VAX;
07525 
07526    /* ASCII format */
07527    if (remote_hw_type & DR_ASCII)
07528       *convert_flags |= CF_ASCII;
07529 }
07530 
07531 /********************************************************************/
07532 void rpc_get_convert_flags(INT * convert_flags)
07533 {
07534    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE), _server_connection.remote_hw_type, convert_flags);
07535 }
07536 
07537 /********************************************************************/
07538 void rpc_ieee2vax_float(float *var)
07539 {
07540    unsigned short int lo, hi;
07541 
07542    /* swap hi and lo word */
07543    lo = *((short int *) (var) + 1);
07544    hi = *((short int *) (var));
07545 
07546    /* correct exponent */
07547    if (lo != 0)
07548       lo += 0x100;
07549 
07550    *((short int *) (var) + 1) = hi;
07551    *((short int *) (var)) = lo;
07552 }
07553 
07554 void rpc_vax2ieee_float(float *var)
07555 {
07556    unsigned short int lo, hi;
07557 
07558    /* swap hi and lo word */
07559    lo = *((short int *) (var) + 1);
07560    hi = *((short int *) (var));
07561 
07562    /* correct exponent */
07563    if (hi != 0)
07564       hi -= 0x100;
07565 
07566    *((short int *) (var) + 1) = hi;
07567    *((short int *) (var)) = lo;
07568 
07569 }
07570 
07571 void rpc_vax2ieee_double(double *var)
07572 {
07573    unsigned short int i1, i2, i3, i4;
07574 
07575    /* swap words */
07576    i1 = *((short int *) (var) + 3);
07577    i2 = *((short int *) (var) + 2);
07578    i3 = *((short int *) (var) + 1);
07579    i4 = *((short int *) (var));
07580 
07581    /* correct exponent */
07582    if (i4 != 0)
07583       i4 -= 0x20;
07584 
07585    *((short int *) (var) + 3) = i4;
07586    *((short int *) (var) + 2) = i3;
07587    *((short int *) (var) + 1) = i2;
07588    *((short int *) (var)) = i1;
07589 }
07590 
07591 void rpc_ieee2vax_double(double *var)
07592 {
07593    unsigned short int i1, i2, i3, i4;
07594 
07595    /* swap words */
07596    i1 = *((short int *) (var) + 3);
07597    i2 = *((short int *) (var) + 2);
07598    i3 = *((short int *) (var) + 1);
07599    i4 = *((short int *) (var));
07600 
07601    /* correct exponent */
07602    if (i1 != 0)
07603       i1 += 0x20;
07604 
07605    *((short int *) (var) + 3) = i4;
07606    *((short int *) (var) + 2) = i3;
07607    *((short int *) (var) + 1) = i2;
07608    *((short int *) (var)) = i1;
07609 }
07610 
07611 /********************************************************************/
07612 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
07613 {
07614 
07615    if (convert_flags & CF_ENDIAN) {
07616       if (tid == TID_WORD || tid == TID_SHORT)
07617          WORD_SWAP(data);
07618       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
07619          DWORD_SWAP(data);
07620       if (tid == TID_DOUBLE)
07621          QWORD_SWAP(data);
07622    }
07623 
07624    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
07625        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
07626       if (tid == TID_FLOAT)
07627          rpc_ieee2vax_float((float *) data);
07628       if (tid == TID_DOUBLE)
07629          rpc_ieee2vax_double((double *) data);
07630    }
07631 
07632    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
07633        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
07634       if (tid == TID_FLOAT)
07635          rpc_vax2ieee_float((float *) data);
07636       if (tid == TID_DOUBLE)
07637          rpc_vax2ieee_double((double *) data);
07638    }
07639 }
07640 
07641 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
07642 /********************************************************************\
07643 
07644   Routine: rpc_convert_data
07645 
07646   Purpose: Convert data format between differenct computers
07647 
07648   Input:
07649     void   *data            Pointer to data
07650     INT    tid              Type ID of data, one of TID_xxx
07651     INT    flags            Combination of following flags:
07652                               RPC_IN: data is input parameter
07653                               RPC_OUT: data is output variable
07654                               RPC_FIXARRAY, RPC_VARARRAY: data is array
07655                                 of "size" bytes (see next param.)
07656                               RPC_OUTGOING: data is outgoing
07657     INT    total_size       Size of bytes of data. Used for variable
07658                             length arrays.
07659     INT    convert_flags    Flags for data conversion
07660 
07661   Output:
07662     void   *data            Is converted according to _convert_flag
07663                             value
07664 
07665   Function value:
07666     RPC_SUCCESS             Successful completion
07667 
07668 \********************************************************************/
07669 {
07670    INT i, n, single_size;
07671    char *p;
07672 
07673    /* convert array */
07674    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
07675       single_size = tid_size[tid];
07676       /* don't convert TID_ARRAY & TID_STRUCT */
07677       if (single_size == 0)
07678          return;
07679 
07680       n = total_size / single_size;
07681 
07682       for (i = 0; i < n; i++) {
07683          p = (char *) data + (i * single_size);
07684          rpc_convert_single(p, tid, flags, convert_flags);
07685       }
07686    } else {
07687       rpc_convert_single(data, tid, flags, convert_flags);
07688    }
07689 }
07690 
07691 /********************************************************************\
07692 *                       type ID functions                            *
07693 \********************************************************************/
07694 
07695 INT rpc_tid_size(INT id)
07696 {
07697    if (id >=0 && id < TID_LAST)
07698       return tid_size[id];
07699 
07700    return 0;
07701 }
07702 
07703 char *rpc_tid_name(INT id)
07704 {
07705    if (id >= 0 && id < TID_LAST)
07706       return tid_name[id];
07707    else
07708       return "<unknown>";
07709 }
07710 
07711 
07712 /**dox***************************************************************/
07713 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07714 
07715 /********************************************************************\
07716 *                        client functions                            *
07717 \********************************************************************/
07718 
07719 /********************************************************************/
07720 /**
07721 Register RPC client for standalone mode (without standard
07722            midas server)
07723 @param list           Array of RPC_LIST structures containing
07724                             function IDs and parameter definitions.
07725                             The end of the list must be indicated by
07726                             a function ID of zero.
07727 @param name          Name of this client
07728 @return RPC_SUCCESS
07729 */
07730 INT rpc_register_client(char *name, RPC_LIST * list)
07731 {
07732    rpc_set_name(name);
07733    rpc_register_functions(rpc_get_internal_list(0), NULL);
07734    rpc_register_functions(list, NULL);
07735 
07736    return RPC_SUCCESS;
07737 }
07738 
07739 /********************************************************************/
07740 /**
07741 Register a set of RPC functions (both as clients or servers)
07742 @param new_list       Array of RPC_LIST structures containing
07743                             function IDs and parameter definitions.
07744                             The end of the list must be indicated by
07745                             a function ID of zero.
07746 @param func          Default dispatch function
07747 
07748 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
07749 */
07750 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
07751 {
07752    INT i, j, iold, inew;
07753 
07754    /* count number of new functions */
07755    for (i = 0; new_list[i].id != 0; i++) {
07756       /* check double defined functions */
07757       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
07758          if (rpc_list[j].id == new_list[i].id)
07759             return RPC_DOUBLE_DEFINED;
07760    }
07761    inew = i;
07762 
07763    /* count number of existing functions */
07764    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
07765    iold = i;
07766 
07767    /* allocate new memory for rpc_list */
07768    if (rpc_list == NULL)
07769       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
07770    else
07771       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
07772 
07773    if (rpc_list == NULL) {
07774       cm_msg(MERROR, "rpc_register_functions", "out of memory");
07775       return RPC_NO_MEMORY;
07776    }
07777 
07778    /* append new functions */
07779    for (i = iold; i < iold + inew; i++) {
07780       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
07781 
07782       /* set default dispatcher */
07783       if (rpc_list[i].dispatch == NULL)
07784          rpc_list[i].dispatch = func;
07785 
07786       /* check valid ID for user functions */
07787       if (new_list != rpc_get_internal_list(0) &&
07788           new_list != rpc_get_internal_list(1) &&
07789           (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
07790          cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID");
07791    }
07792 
07793    /* mark end of list */
07794    rpc_list[i].id = 0;
07795 
07796    return RPC_SUCCESS;
07797 }
07798 
07799 
07800 
07801 /**dox***************************************************************/
07802 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07803 
07804 /********************************************************************/
07805 INT rpc_deregister_functions()
07806 /********************************************************************\
07807 
07808   Routine: rpc_deregister_functions
07809 
07810   Purpose: Free memory of previously registered functions
07811 
07812   Input:
07813     none
07814 
07815   Output:
07816     none
07817 
07818   Function value:
07819     RPC_SUCCESS              Successful completion
07820 
07821 \********************************************************************/
07822 {
07823    if (rpc_list)
07824       M_FREE(rpc_list);
07825    rpc_list = NULL;
07826 
07827    return RPC_SUCCESS;
07828 }
07829 
07830 
07831 /********************************************************************/
07832 INT rpc_register_function(INT id, INT(*func) (INT, void **))
07833 /********************************************************************\
07834 
07835   Routine: rpc_register_function
07836 
07837   Purpose: Replace a dispatch function for a specific rpc routine
07838 
07839   Input:
07840     INT      id             RPC ID
07841     INT      *func          New dispatch function
07842 
07843   Output:
07844    <implicit: func gets copied to rpc_list>
07845 
07846   Function value:
07847    RPC_SUCCESS              Successful completion
07848    RPC_INVALID_ID           RPC ID not found
07849 
07850 \********************************************************************/
07851 {
07852    INT i;
07853 
07854    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
07855       if (rpc_list[i].id == id)
07856          break;
07857 
07858    if (rpc_list[i].id == id)
07859       rpc_list[i].dispatch = func;
07860    else
07861       return RPC_INVALID_ID;
07862 
07863    return RPC_SUCCESS;
07864 }
07865 
07866 
07867 /********************************************************************/
07868 INT rpc_client_dispatch(int sock)
07869 /********************************************************************\
07870 
07871   Routine: rpc_client_dispatch
07872 
07873   Purpose: Gets called whenever a client receives data from the
07874            server. Get set via rpc_connect. Internal use only.
07875 
07876 \********************************************************************/
07877 {
07878    INT hDB, hKey, n;
07879    NET_COMMAND *nc;
07880    INT status = 0;
07881    char net_buffer[256];
07882 
07883    nc = (NET_COMMAND *) net_buffer;
07884 
07885    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07886    if (n <= 0)
07887       return SS_ABORT;
07888 
07889    if (nc->header.routine_id == MSG_ODB) {
07890       /* update a changed record */
07891       hDB = *((INT *) nc->param);
07892       hKey = *((INT *) nc->param + 1);
07893       status = db_update_record(hDB, hKey, 0);
07894    }
07895 
07896    else if (nc->header.routine_id == MSG_WATCHDOG) {
07897       nc->header.routine_id = 1;
07898       nc->header.param_size = 0;
07899       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07900       status = RPC_SUCCESS;
07901    }
07902 
07903    else if (nc->header.routine_id == MSG_BM) {
07904       fd_set readfds;
07905       struct timeval timeout;
07906 
07907       /* receive further messages to empty TCP queue */
07908       do {
07909          FD_ZERO(&readfds);
07910          FD_SET(sock, &readfds);
07911 
07912          timeout.tv_sec = 0;
07913          timeout.tv_usec = 0;
07914 
07915          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
07916 
07917          if (FD_ISSET(sock, &readfds)) {
07918             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
07919             if (n <= 0)
07920                return SS_ABORT;
07921 
07922             if (nc->header.routine_id == MSG_ODB) {
07923                /* update a changed record */
07924                hDB = *((INT *) nc->param);
07925                hKey = *((INT *) nc->param + 1);
07926                status = db_update_record(hDB, hKey, 0);
07927             }
07928 
07929             else if (nc->header.routine_id == MSG_WATCHDOG) {
07930                nc->header.routine_id = 1;
07931                nc->header.param_size = 0;
07932                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
07933                status = RPC_SUCCESS;
07934             }
07935          }
07936 
07937       } while (FD_ISSET(sock, &readfds));
07938 
07939       /* poll event from server */
07940       status = bm_poll_event(FALSE);
07941    }
07942 
07943    return status;
07944 }
07945 
07946 
07947 /********************************************************************/
07948 INT rpc_client_connect(char *host_name, INT port, char *client_name, HNDLE * hConnection)
07949 /********************************************************************\
07950 
07951   Routine: rpc_client_connect
07952 
07953   Purpose: Establish a network connection to a remote client
07954 
07955   Input:
07956     char *host_name          IP address of host to connect to.
07957     INT  port                TPC port to connect to.
07958     char *clinet_name        Client program name
07959 
07960   Output:
07961     HNDLE *hConnection       Handle for new connection which can be used
07962                              in future rpc_call(hConnection....) calls
07963 
07964   Function value:
07965     RPC_SUCCESS              Successful completion
07966     RPC_NET_ERROR            Error in socket call
07967     RPC_NO_CONNECTION        Maximum number of connections reached
07968     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
07969 
07970 \********************************************************************/
07971 {
07972    INT i, status, idx;
07973    struct sockaddr_in bind_addr;
07974    INT sock;
07975    INT remote_hw_type, hw_type;
07976    char str[200];
07977    char version[32], v1[32];
07978    char local_prog_name[NAME_LENGTH];
07979    char local_host_name[HOST_NAME_LENGTH];
07980    struct hostent *phe;
07981 
07982 #ifdef OS_WINNT
07983    {
07984       WSADATA WSAData;
07985 
07986       /* Start windows sockets */
07987       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
07988          return RPC_NET_ERROR;
07989    }
07990 #endif
07991 
07992    /* check if cm_connect_experiment was called */
07993    if (_client_name[0] == 0) {
07994       cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
07995       return RPC_NOT_REGISTERED;
07996    }
07997 
07998    /* check for broken connections */
07999    rpc_client_check();
08000 
08001    /* check if connection already exists */
08002    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08003       if (_client_connection[i].send_sock != 0 &&
08004           strcmp(_client_connection[i].host_name, host_name) == 0 && _client_connection[i].port == port) {
08005          *hConnection = i + 1;
08006          return RPC_SUCCESS;
08007       }
08008 
08009    /* search for free entry */
08010    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08011       if (_client_connection[i].send_sock == 0)
08012          break;
08013 
08014    /* open new network connection */
08015    if (i == MAX_RPC_CONNECTION) {
08016       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
08017       return RPC_NO_CONNECTION;
08018    }
08019 
08020    /* create a new socket for connecting to remote server */
08021    sock = socket(AF_INET, SOCK_STREAM, 0);
08022    if (sock == -1) {
08023       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
08024       return RPC_NET_ERROR;
08025    }
08026 
08027    idx = i;
08028    strcpy(_client_connection[idx].host_name, host_name);
08029    strcpy(_client_connection[idx].client_name, client_name);
08030    _client_connection[idx].port = port;
08031    _client_connection[idx].exp_name[0] = 0;
08032    _client_connection[idx].transport = RPC_TCP;
08033    _client_connection[idx].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08034    _client_connection[idx].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08035 
08036    /* connect to remote node */
08037    memset(&bind_addr, 0, sizeof(bind_addr));
08038    bind_addr.sin_family = AF_INET;
08039    bind_addr.sin_addr.s_addr = 0;
08040    bind_addr.sin_port = htons((short) port);
08041 
08042 #ifdef OS_VXWORKS
08043    {
08044       INT host_addr;
08045 
08046       host_addr = hostGetByName(host_name);
08047       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08048    }
08049 #else
08050    phe = gethostbyname(host_name);
08051    if (phe == NULL) {
08052       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
08053       return RPC_NET_ERROR;
08054    }
08055    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08056 #endif
08057 
08058 #ifdef OS_UNIX
08059    do {
08060       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
08061 
08062       /* don't return if an alarm signal was cought */
08063    } while (status == -1 && errno == EINTR);
08064 #else
08065    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08066 #endif
08067 
08068    if (status != 0) {
08069       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
08070          message should be displayed by application */
08071       return RPC_NET_ERROR;
08072    }
08073 
08074    /* set TCP_NODELAY option for better performance */
08075    i = 1;
08076    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
08077 
08078    /* send local computer info */
08079    rpc_get_name(local_prog_name);
08080    gethostname(local_host_name, sizeof(local_host_name));
08081 
08082    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08083    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name, local_host_name);
08084 
08085    send(sock, str, strlen(str) + 1, 0);
08086 
08087    /* receive remote computer info */
08088    i = recv_string(sock, str, sizeof(str), 10000);
08089    if (i <= 0) {
08090       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s", str);
08091       return RPC_NET_ERROR;
08092    }
08093 
08094    remote_hw_type = version[0] = 0;
08095    sscanf(str, "%d %s", &remote_hw_type, version);
08096    _client_connection[idx].remote_hw_type = remote_hw_type;
08097    _client_connection[idx].send_sock = sock;
08098 
08099    /* print warning if version patch level doesn't agree */
08100    strcpy(v1, version);
08101    if (strchr(v1, '.'))
08102       if (strchr(strchr(v1, '.') + 1, '.'))
08103          *strchr(strchr(v1, '.') + 1, '.') = 0;
08104 
08105    strcpy(str, cm_get_version());
08106    if (strchr(str, '.'))
08107       if (strchr(strchr(str, '.') + 1, '.'))
08108          *strchr(strchr(str, '.') + 1, '.') = 0;
08109 
08110    if (strcmp(v1, str) != 0) {
08111       sprintf(str, "remote MIDAS version %s differs from local version %s", version, cm_get_version());
08112       cm_msg(MERROR, "rpc_client_connect", str);
08113    }
08114 
08115    *hConnection = idx + 1;
08116 
08117    return RPC_SUCCESS;
08118 }
08119 
08120 /********************************************************************/
08121 void rpc_client_check()
08122 /********************************************************************\
08123 
08124   Routine: rpc_client_check
08125 
08126   Purpose: Check all client connections if remote client closed link
08127 
08128   Function value:
08129     RPC_SUCCESS              Successful completion
08130     RPC_NET_ERROR            Error in socket call
08131     RPC_NO_CONNECTION        Maximum number of connections reached
08132     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08133 
08134 \********************************************************************/
08135 {
08136    INT i, status;
08137 
08138    /* check for broken connections */
08139    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08140       if (_client_connection[i].send_sock != 0) {
08141          int sock;
08142          fd_set readfds;
08143          struct timeval timeout;
08144          char buffer[64];
08145 
08146          sock = _client_connection[i].send_sock;
08147          FD_ZERO(&readfds);
08148          FD_SET(sock, &readfds);
08149 
08150          timeout.tv_sec = 0;
08151          timeout.tv_usec = 0;
08152 
08153          do {
08154             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08155          } while (status == -1);        /* dont return if an alarm signal was cought */
08156 
08157          if (FD_ISSET(sock, &readfds)) {
08158             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
08159 
08160             if (equal_ustring(buffer, "EXIT")) {
08161                /* normal exit */
08162                closesocket(sock);
08163                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08164             }
08165 
08166             if (status <= 0) {
08167                cm_msg(MERROR, "rpc_client_check",
08168                       "Connection broken to \"%s\" on host %s",
08169                       _client_connection[i].client_name, _client_connection[i].host_name);
08170 
08171                /* connection broken -> reset */
08172                closesocket(sock);
08173                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08174             }
08175          }
08176       }
08177 }
08178 
08179 
08180 /********************************************************************/
08181 INT rpc_server_connect(char *host_name, char *exp_name)
08182 /********************************************************************\
08183 
08184   Routine: rpc_server_connect
08185 
08186   Purpose: Extablish a network connection to a remote MIDAS
08187            server using a callback scheme.
08188 
08189   Input:
08190     char *host_name         IP address of host to connect to.
08191 
08192     INT  port               TPC port to connect to.
08193 
08194     char *exp_name          Name of experiment to connect to. By using
08195                             this name, several experiments (e.g. online
08196                             DAQ and offline analysis) can run simultan-
08197                             eously on the same host.
08198 
08199   Output:
08200     none
08201 
08202   Function value:
08203     RPC_SUCCESS              Successful completion
08204     RPC_NET_ERROR            Error in socket call
08205     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08206     CM_UNDEF_EXP             Undefined experiment on server
08207 
08208 \********************************************************************/
08209 {
08210    INT i, status, flag;
08211    struct sockaddr_in bind_addr;
08212    INT sock, lsock1, lsock2, lsock3;
08213    INT listen_port1, listen_port2, listen_port3;
08214    INT remote_hw_type, hw_type;
08215    unsigned int size;
08216    char str[200], version[32], v1[32];
08217    char local_prog_name[NAME_LENGTH];
08218    struct hostent *phe;
08219    fd_set readfds;
08220    struct timeval timeout;
08221 
08222 #ifdef OS_WINNT
08223    {
08224       WSADATA WSAData;
08225 
08226       /* Start windows sockets */
08227       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08228          return RPC_NET_ERROR;
08229    }
08230 #endif
08231 
08232    /* check if local connection */
08233    if (host_name[0] == 0)
08234       return RPC_SUCCESS;
08235 
08236    /* register system functions */
08237    rpc_register_functions(rpc_get_internal_list(0), NULL);
08238 
08239    /* check if cm_connect_experiment was called */
08240    if (_client_name[0] == 0) {
08241       cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
08242       return RPC_NOT_REGISTERED;
08243    }
08244 
08245    /* check if connection already exists */
08246    if (_server_connection.send_sock != 0)
08247       return RPC_SUCCESS;
08248 
08249    strcpy(_server_connection.host_name, host_name);
08250    strcpy(_server_connection.exp_name, exp_name);
08251    _server_connection.transport = RPC_TCP;
08252    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08253 
08254    /* create new TCP sockets for listening */
08255    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08256    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08257    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08258    if (lsock3 == -1) {
08259       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08260       return RPC_NET_ERROR;
08261    }
08262 
08263    flag = 1;
08264    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08265    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08266    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08267 
08268    /* let OS choose any port number */
08269    memset(&bind_addr, 0, sizeof(bind_addr));
08270    bind_addr.sin_family = AF_INET;
08271    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08272    bind_addr.sin_port = 0;
08273 
08274    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08275    bind_addr.sin_port = 0;
08276    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08277    bind_addr.sin_port = 0;
08278    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08279    if (status < 0) {
08280       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08281       return RPC_NET_ERROR;
08282    }
08283 
08284    /* listen for connection */
08285    status = listen(lsock1, 1);
08286    status = listen(lsock2, 1);
08287    status = listen(lsock3, 1);
08288    if (status < 0) {
08289       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08290       return RPC_NET_ERROR;
08291    }
08292 
08293    /* find out which port OS has chosen */
08294    size = sizeof(bind_addr);
08295    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08296    listen_port1 = ntohs(bind_addr.sin_port);
08297    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08298    listen_port2 = ntohs(bind_addr.sin_port);
08299    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08300    listen_port3 = ntohs(bind_addr.sin_port);
08301 
08302    /* create a new socket for connecting to remote server */
08303    sock = socket(AF_INET, SOCK_STREAM, 0);
08304    if (sock == -1) {
08305       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08306       return RPC_NET_ERROR;
08307    }
08308 
08309    /* connect to remote node */
08310    memset(&bind_addr, 0, sizeof(bind_addr));
08311    bind_addr.sin_family = AF_INET;
08312    bind_addr.sin_addr.s_addr = 0;
08313    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08314 
08315 #ifdef OS_VXWORKS
08316    {
08317       INT host_addr;
08318 
08319       host_addr = hostGetByName(host_name);
08320       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08321    }
08322 #else
08323    phe = gethostbyname(host_name);
08324    if (phe == NULL) {
08325       cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08326       return RPC_NET_ERROR;
08327    }
08328    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08329 #endif
08330 
08331 #ifdef OS_UNIX
08332    do {
08333       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08334 
08335       /* don't return if an alarm signal was cought */
08336    } while (status == -1 && errno == EINTR);
08337 #else
08338    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08339 #endif
08340 
08341    if (status != 0) {
08342 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08343       return RPC_NET_ERROR;
08344    }
08345 
08346    /* connect to experiment */
08347    if (exp_name[0] == 0)
08348       sprintf(str, "C %d %d %d %s Default", listen_port1, listen_port2, listen_port3, cm_get_version());
08349    else
08350       sprintf(str, "C %d %d %d %s %s", listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08351 
08352    send(sock, str, strlen(str) + 1, 0);
08353    i = recv_string(sock, str, sizeof(str), 10000);
08354    closesocket(sock);
08355    if (i <= 0) {
08356       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08357       return RPC_NET_ERROR;
08358    }
08359 
08360    status = version[0] = 0;
08361    sscanf(str, "%d %s", &status, version);
08362 
08363    if (status == 2) {
08364 /*  message "undefined experiment" should be displayed by application */
08365       return CM_UNDEF_EXP;
08366    }
08367 
08368    /* print warning if version patch level doesn't agree */
08369    strcpy(v1, version);
08370    if (strchr(v1, '.'))
08371       if (strchr(strchr(v1, '.') + 1, '.'))
08372          *strchr(strchr(v1, '.') + 1, '.') = 0;
08373 
08374    strcpy(str, cm_get_version());
08375    if (strchr(str, '.'))
08376       if (strchr(strchr(str, '.') + 1, '.'))
08377          *strchr(strchr(str, '.') + 1, '.') = 0;
08378 
08379    if (strcmp(v1, str) != 0) {
08380       sprintf(str, "remote MIDAS version %s differs from local version %s", version, cm_get_version());
08381       cm_msg(MERROR, "rpc_server_connect", str);
08382    }
08383 
08384    /* wait for callback on send and recv socket with timeout */
08385    FD_ZERO(&readfds);
08386    FD_SET(lsock1, &readfds);
08387    FD_SET(lsock2, &readfds);
08388    FD_SET(lsock3, &readfds);
08389 
08390    timeout.tv_sec = 5;
08391    timeout.tv_usec = 0;
08392 
08393    do {
08394       status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08395 
08396       /* if an alarm signal was cought, restart select with reduced timeout */
08397       if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
08398          timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
08399 
08400    } while (status == -1);      /* dont return if an alarm signal was cought */
08401 
08402    if (!FD_ISSET(lsock1, &readfds)) {
08403       cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
08404       closesocket(lsock1);
08405       closesocket(lsock2);
08406       closesocket(lsock3);
08407       return RPC_NET_ERROR;
08408    }
08409 
08410    size = sizeof(bind_addr);
08411 
08412    _server_connection.send_sock = accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08413 
08414    _server_connection.recv_sock = accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08415 
08416    _server_connection.event_sock = accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08417 
08418    if (_server_connection.send_sock == -1 ||
08419        _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
08420       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08421       return RPC_NET_ERROR;
08422    }
08423 
08424    closesocket(lsock1);
08425    closesocket(lsock2);
08426    closesocket(lsock3);
08427 
08428    /* set TCP_NODELAY option for better performance */
08429    flag = 1;
08430    setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08431    setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08432 
08433    /* increase send buffer size to 64kB */
08434    flag = 0x10000;
08435    setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
08436 
08437    /* send local computer info */
08438    rpc_get_name(local_prog_name);
08439    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08440    sprintf(str, "%d %s", hw_type, local_prog_name);
08441 
08442    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08443 
08444    /* receive remote computer info */
08445    i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
08446    if (i <= 0) {
08447       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
08448       return RPC_NET_ERROR;
08449    }
08450 
08451    sscanf(str, "%d", &remote_hw_type);
08452    _server_connection.remote_hw_type = remote_hw_type;
08453 
08454    /* set dispatcher which receives database updates */
08455    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection, (int (*)(void)) rpc_client_dispatch);
08456 
08457    return RPC_SUCCESS;
08458 }
08459 
08460 /********************************************************************/
08461 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08462 /********************************************************************\
08463 
08464   Routine: rpc_client_disconnect
08465 
08466   Purpose: Close a rpc connection to a MIDAS client
08467 
08468   Input:
08469     HNDLE  hConn           Handle of connection
08470     BOOL   bShutdown       Shut down remote server if TRUE
08471 
08472   Output:
08473     none
08474 
08475   Function value:
08476    RPC_SUCCESS             Successful completion
08477 
08478 \********************************************************************/
08479 {
08480    INT i;
08481 
08482    if (hConn == -1) {
08483       /* close all open connections */
08484       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
08485          if (_client_connection[i].send_sock != 0)
08486             rpc_client_disconnect(i + 1, FALSE);
08487 
08488       /* close server connection from other clients */
08489       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08490          if (_server_acception[i].recv_sock) {
08491             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
08492             closesocket(_server_acception[i].recv_sock);
08493          }
08494    } else {
08495       /* notify server about exit */
08496 
08497       /* set FTCP mode (helps for rebooted VxWorks nodes) */
08498       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
08499       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
08500 
08501       /* close socket */
08502       if (_client_connection[hConn - 1].send_sock)
08503          closesocket(_client_connection[hConn - 1].send_sock);
08504 
08505       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
08506    }
08507 
08508    return RPC_SUCCESS;
08509 }
08510 
08511 
08512 /********************************************************************/
08513 INT rpc_server_disconnect()
08514 /********************************************************************\
08515 
08516   Routine: rpc_server_disconnect
08517 
08518   Purpose: Close a rpc connection to a MIDAS server and close all
08519            server connections from other clients
08520 
08521   Input:
08522     none
08523 
08524   Output:
08525     none
08526 
08527   Function value:
08528    RPC_SUCCESS             Successful completion
08529    RPC_NET_ERROR           Error in socket call
08530    RPC_NO_CONNECTION       Maximum number of connections reached
08531 
08532 \********************************************************************/
08533 {
08534    static int rpc_server_disconnect_recursion_level = 0;
08535 
08536    if (rpc_server_disconnect_recursion_level)
08537       return RPC_SUCCESS;
08538 
08539    rpc_server_disconnect_recursion_level = 1;
08540 
08541    /* flush remaining events */
08542    rpc_flush_event();
08543 
08544    /* notify server about exit */
08545    rpc_call(RPC_ID_EXIT);
08546 
08547    /* close sockets */
08548    closesocket(_server_connection.send_sock);
08549    closesocket(_server_connection.recv_sock);
08550    closesocket(_server_connection.event_sock);
08551 
08552    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
08553 
08554    rpc_server_disconnect_recursion_level = 0;
08555    return RPC_SUCCESS;
08556 }
08557 
08558 
08559 /********************************************************************/
08560 INT rpc_is_remote(void)
08561 /********************************************************************\
08562 
08563   Routine: rpc_is_remote
08564 
08565   Purpose: Return true if program is connected to a remote server
08566 
08567   Input:
08568    none
08569 
08570   Output:
08571     none
08572 
08573   Function value:
08574     INT    RPC connection index
08575 
08576 \********************************************************************/
08577 {
08578    return _server_connection.send_sock != 0;
08579 }
08580 
08581 
08582 /********************************************************************/
08583 INT rpc_get_server_acception(void)
08584 /********************************************************************\
08585 
08586   Routine: rpc_get_server_acception
08587 
08588   Purpose: Return actual RPC server connection index
08589 
08590   Input:
08591    none
08592 
08593   Output:
08594     none
08595 
08596   Function value:
08597     INT    RPC server connection index
08598 
08599 \********************************************************************/
08600 {
08601    return _server_acception_index;
08602 }
08603 
08604 
08605 /********************************************************************/
08606 INT rpc_set_server_acception(INT idx)
08607 /********************************************************************\
08608 
08609   Routine: rpc_set_server_acception
08610 
08611   Purpose: Set actual RPC server connection index
08612 
08613   Input:
08614     INT  idx                Server index
08615 
08616   Output:
08617     none
08618 
08619   Function value:
08620     RPC_SUCCESS             Successful completion
08621 
08622 \********************************************************************/
08623 {
08624    _server_acception_index = idx;
08625    return RPC_SUCCESS;
08626 }
08627 
08628 
08629 /********************************************************************/
08630 INT rpc_get_option(HNDLE hConn, INT item)
08631 /********************************************************************\
08632 
08633   Routine: rpc_get_option
08634 
08635   Purpose: Get actual RPC option
08636 
08637   Input:
08638     HNDLE hConn             RPC connection handle
08639     INT   item              One of RPC_Oxxx
08640 
08641   Output:
08642     none
08643 
08644   Function value:
08645     INT                     Actual option
08646 
08647 \********************************************************************/
08648 {
08649    switch (item) {
08650    case RPC_OTIMEOUT:
08651       if (hConn == -1)
08652          return _server_connection.rpc_timeout;
08653       return _client_connection[hConn - 1].rpc_timeout;
08654 
08655    case RPC_OTRANSPORT:
08656       if (hConn == -1)
08657          return _server_connection.transport;
08658       return _client_connection[hConn - 1].transport;
08659 
08660    case RPC_OHW_TYPE:
08661       {
08662          INT tmp_type, size;
08663          DWORD dummy;
08664          unsigned char *p;
08665          float f;
08666          double d;
08667 
08668          tmp_type = 0;
08669 
08670          /* test pointer size */
08671          size = sizeof(p);
08672          if (size == 2)
08673             tmp_type |= DRI_16;
08674          if (size == 4)
08675             tmp_type |= DRI_32;
08676          if (size == 8)
08677             tmp_type |= DRI_64;
08678 
08679          /* test if little or big endian machine */
08680          dummy = 0x12345678;
08681          p = (unsigned char *) &dummy;
08682          if (*p == 0x78)
08683             tmp_type |= DRI_LITTLE_ENDIAN;
08684          else if (*p == 0x12)
08685             tmp_type |= DRI_BIG_ENDIAN;
08686          else
08687             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
08688 
08689          /* floating point format */
08690          f = (float) 1.2345;
08691          dummy = 0;
08692          memcpy(&dummy, &f, sizeof(f));
08693          if ((dummy & 0xFF) == 0x19 &&
08694              ((dummy >> 8) & 0xFF) == 0x04 &&
08695              ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
08696             tmp_type |= DRF_IEEE;
08697          else if ((dummy & 0xFF) == 0x9E &&
08698                   ((dummy >> 8) & 0xFF) == 0x40 &&
08699                   ((dummy >> 16) & 0xFF) == 0x19 && ((dummy >> 24) & 0xFF) == 0x04)
08700             tmp_type |= DRF_G_FLOAT;
08701          else
08702             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08703 
08704          d = (double) 1.2345;
08705          dummy = 0;
08706          memcpy(&dummy, &d, sizeof(f));
08707          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
08708              ((dummy >> 8) & 0xFF) == 0x97 &&
08709              ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
08710             tmp_type |= DRF_IEEE;
08711          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
08712                   ((dummy >> 8) & 0xFF) == 0xC0 &&
08713                   ((dummy >> 16) & 0xFF) == 0xF3 && ((dummy >> 24) & 0xFF) == 0x3F)
08714             tmp_type |= DRF_IEEE;
08715          else if ((dummy & 0xFF) == 0x13 &&
08716                   ((dummy >> 8) & 0xFF) == 0x40 &&
08717                   ((dummy >> 16) & 0xFF) == 0x83 && ((dummy >> 24) & 0xFF) == 0xC0)
08718             tmp_type |= DRF_G_FLOAT;
08719          else if ((dummy & 0xFF) == 0x9E &&
08720                   ((dummy >> 8) & 0xFF) == 0x40 &&
08721                   ((dummy >> 16) & 0xFF) == 0x18 && ((dummy >> 24) & 0xFF) == 0x04)
08722             cm_msg(MERROR, "rpc_get_option",
08723                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
08724          else
08725             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08726 
08727          return tmp_type;
08728       }
08729 
08730    default:
08731       cm_msg(MERROR, "rpc_get_option", "invalid argument");
08732       break;
08733    }
08734 
08735    return 0;
08736 }
08737 
08738 /**dox***************************************************************/
08739 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08740 
08741 /********************************************************************/
08742 /**
08743 Set RPC option
08744 @param hConn              RPC connection handle
08745 @param item               One of RPC_Oxxx
08746 @param value              Value to set
08747 @return RPC_SUCCESS
08748 */
08749 INT rpc_set_option(HNDLE hConn, INT item, INT value)
08750 {
08751    switch (item) {
08752    case RPC_OTIMEOUT:
08753       if (hConn == -1)
08754          _server_connection.rpc_timeout = value;
08755       else
08756          _client_connection[hConn - 1].rpc_timeout = value;
08757       break;
08758 
08759    case RPC_OTRANSPORT:
08760       if (hConn == -1)
08761          _server_connection.transport = value;
08762       else
08763          _client_connection[hConn - 1].transport = value;
08764       break;
08765 
08766    case RPC_NODELAY:
08767       if (hConn == -1)
08768          setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
08769       else
08770          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP,
08771                     TCP_NODELAY, (char *) &value, sizeof(value));
08772       break;
08773 
08774    default:
08775       cm_msg(MERROR, "rpc_set_option", "invalid argument");
08776       break;
08777    }
08778 
08779    return 0;
08780 }
08781 
08782 
08783 /**dox***************************************************************/
08784 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08785 
08786 /********************************************************************/
08787 POINTER_T rpc_get_server_option(INT item)
08788 /********************************************************************\
08789 
08790   Routine: rpc_get_server_option
08791 
08792   Purpose: Get actual RPC option for server connection
08793 
08794   Input:
08795     INT  item               One of RPC_Oxxx
08796 
08797   Output:
08798     none
08799 
08800   Function value:
08801     INT                     Actual option
08802 
08803 \********************************************************************/
08804 {
08805    INT i;
08806 
08807    if (item == RPC_OSERVER_TYPE)
08808       return _server_type;
08809 
08810    if (item == RPC_OSERVER_NAME)
08811       return (POINTER_T) _server_name;
08812 
08813    /* return 0 for local calls */
08814    if (_server_type == ST_NONE)
08815       return 0;
08816 
08817    /* check which connections belongs to caller */
08818    if (_server_type == ST_MTHREAD) {
08819       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08820          if (_server_acception[i].tid == ss_gettid())
08821             break;
08822    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08823       i = MAX(0, _server_acception_index - 1);
08824    else
08825       i = 0;
08826 
08827    switch (item) {
08828    case RPC_CONVERT_FLAGS:
08829       return _server_acception[i].convert_flags;
08830    case RPC_ODB_HANDLE:
08831       return _server_acception[i].odb_handle;
08832    case RPC_CLIENT_HANDLE:
08833       return _server_acception[i].client_handle;
08834    case RPC_SEND_SOCK:
08835       return _server_acception[i].send_sock;
08836    case RPC_WATCHDOG_TIMEOUT:
08837       return _server_acception[i].watchdog_timeout;
08838    }
08839 
08840    return 0;
08841 }
08842 
08843 
08844 /********************************************************************/
08845 INT rpc_set_server_option(INT item, POINTER_T value)
08846 /********************************************************************\
08847 
08848   Routine: rpc_set_server_option
08849 
08850   Purpose: Set RPC option for server connection
08851 
08852   Input:
08853    INT  item               One of RPC_Oxxx
08854    INT  value              Value to set
08855 
08856   Output:
08857     none
08858 
08859   Function value:
08860     RPC_SUCCESS             Successful completion
08861 
08862 \********************************************************************/
08863 {
08864    INT i;
08865 
08866    if (item == RPC_OSERVER_TYPE) {
08867       _server_type = value;
08868       return RPC_SUCCESS;
08869    }
08870    if (item == RPC_OSERVER_NAME) {
08871       strcpy(_server_name, (char *) value);
08872       return RPC_SUCCESS;
08873    }
08874 
08875    /* check which connections belongs to caller */
08876    if (_server_type == ST_MTHREAD) {
08877       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08878          if (_server_acception[i].tid == ss_gettid())
08879             break;
08880    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
08881       i = MAX(0, _server_acception_index - 1);
08882    else
08883       i = 0;
08884 
08885    switch (item) {
08886    case RPC_CONVERT_FLAGS:
08887       _server_acception[i].convert_flags = value;
08888       break;
08889    case RPC_ODB_HANDLE:
08890       _server_acception[i].odb_handle = value;
08891       break;
08892    case RPC_CLIENT_HANDLE:
08893       _server_acception[i].client_handle = value;
08894       break;
08895    case RPC_WATCHDOG_TIMEOUT:
08896       _server_acception[i].watchdog_timeout = value;
08897       break;
08898    }
08899 
08900    return RPC_SUCCESS;
08901 }
08902 
08903 
08904 /********************************************************************/
08905 INT rpc_get_name(char *name)
08906 /********************************************************************\
08907 
08908   Routine: rpc_get_name
08909 
08910   Purpose: Get name set by rpc_set_name
08911 
08912   Input:
08913     none
08914 
08915   Output:
08916     char*  name             The location pointed by *name receives a
08917                             copy of the _prog_name
08918 
08919   Function value:
08920     RPC_SUCCESS             Successful completion
08921 
08922 \********************************************************************/
08923 {
08924    strcpy(name, _client_name);
08925 
08926    return RPC_SUCCESS;
08927 }
08928 
08929 
08930 /********************************************************************/
08931 INT rpc_set_name(char *name)
08932 /********************************************************************\
08933 
08934   Routine: rpc_set_name
08935 
08936   Purpose: Set name of actual program for further rpc connections
08937 
08938   Input:
08939    char *name               Program name, up to NAME_LENGTH chars,
08940                             no blanks
08941 
08942   Output:
08943     none
08944 
08945   Function value:
08946     RPC_SUCCESS             Successful completion
08947 
08948 \********************************************************************/
08949 {
08950    strcpy(_client_name, name);
08951 
08952    return RPC_SUCCESS;
08953 }
08954 
08955 
08956 /********************************************************************/
08957 INT rpc_set_debug(void (*func) (char *), INT mode)
08958 /********************************************************************\
08959 
08960   Routine: rpc_set_debug
08961 
08962   Purpose: Set a function which is called on every RPC call to
08963            display the function name and parameters of the RPC
08964            call.
08965 
08966   Input:
08967    void *func(char*)        Pointer to function.
08968    INT  mode                Debug mode
08969 
08970   Output:
08971     none
08972 
08973   Function value:
08974     RPC_SUCCESS             Successful completion
08975 
08976 \********************************************************************/
08977 {
08978    _debug_print = func;
08979    _debug_mode = mode;
08980    return RPC_SUCCESS;
08981 }
08982 
08983 /********************************************************************/
08984 void rpc_debug_printf(char *format, ...)
08985 /********************************************************************\
08986 
08987   Routine: rpc_debug_print
08988 
08989   Purpose: Calls function set via rpc_set_debug to output a string.
08990 
08991   Input:
08992    char *str                Debug string
08993 
08994   Output:
08995     none
08996 
08997 \********************************************************************/
08998 {
08999    va_list argptr;
09000    char str[1000];
09001 
09002    if (_debug_mode) {
09003       va_start(argptr, format);
09004       vsprintf(str, (char *) format, argptr);
09005       va_end(argptr);
09006 
09007       if (_debug_print) {
09008          strcat(str, "\n");
09009          _debug_print(str);
09010       } else
09011          puts(str);
09012    }
09013 }
09014 
09015 /********************************************************************/
09016 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
09017 {
09018    switch (arg_type) {
09019       /* On the stack, the minimum parameter size is sizeof(int).
09020          To avoid problems on little endian systems, treat all
09021          smaller parameters as int's */
09022    case TID_BYTE:
09023    case TID_SBYTE:
09024    case TID_CHAR:
09025    case TID_WORD:
09026    case TID_SHORT:
09027       *((int *) arg) = va_arg(*arg_ptr, int);
09028       break;
09029 
09030    case TID_INT:
09031    case TID_BOOL:
09032       *((INT *) arg) = va_arg(*arg_ptr, INT);
09033       break;
09034 
09035    case TID_DWORD:
09036       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
09037       break;
09038 
09039       /* float variables are passed as double by the compiler */
09040    case TID_FLOAT:
09041       *((float *) arg) = (float) va_arg(*arg_ptr, double);
09042       break;
09043 
09044    case TID_DOUBLE:
09045       *((double *) arg) = va_arg(*arg_ptr, double);
09046       break;
09047 
09048    case TID_ARRAY:
09049       *((char **) arg) = va_arg(*arg_ptr, char *);
09050       break;
09051    }
09052 }
09053 
09054 
09055 /********************************************************************/
09056 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
09057 /********************************************************************\
09058 
09059   Routine: rpc_client_call
09060 
09061   Purpose: Call a function on a MIDAS client
09062 
09063   Input:
09064     INT  hConn              Client connection
09065     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09066 
09067     ...                     variable argument list
09068 
09069   Output:
09070     (depends on argument list)
09071 
09072   Function value:
09073     RPC_SUCCESS             Successful completion
09074     RPC_NET_ERROR           Error in socket call
09075     RPC_NO_CONNECTION       No active connection
09076     RPC_TIMEOUT             Timeout in RPC call
09077     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09078     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09079 
09080 \********************************************************************/
09081 {
09082    va_list ap, aptmp;
09083    char arg[8], arg_tmp[8];
09084    INT arg_type, transport, rpc_timeout;
09085    INT i, idx, status, rpc_index;
09086    INT param_size, arg_size, send_size;
09087    INT tid, flags;
09088    fd_set readfds;
09089    struct timeval timeout;
09090    char *param_ptr, str[80];
09091    BOOL bpointer, bbig;
09092    NET_COMMAND *nc;
09093    int send_sock;
09094 
09095    idx = hConn - 1;
09096 
09097    if (_client_connection[idx].send_sock == 0) {
09098       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
09099       return RPC_NO_CONNECTION;
09100    }
09101 
09102    send_sock = _client_connection[idx].send_sock;
09103    rpc_timeout = _client_connection[idx].rpc_timeout;
09104    transport = _client_connection[idx].transport;
09105 
09106    /* init network buffer */
09107    if (_net_send_buffer_size == 0) {
09108       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09109       if (_net_send_buffer == NULL) {
09110          cm_msg(MERROR, "rpc_client_call", "not enough memory to allocate network buffer");
09111          return RPC_EXCEED_BUFFER;
09112       }
09113       _net_send_buffer_size = NET_BUFFER_SIZE;
09114    }
09115 
09116    nc = (NET_COMMAND *) _net_send_buffer;
09117    nc->header.routine_id = routine_id;
09118 
09119    if (transport == RPC_FTCP)
09120       nc->header.routine_id |= TCP_FAST;
09121 
09122    for (i = 0;; i++)
09123       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09124          break;
09125    rpc_index = i;
09126    if (rpc_list[i].id == 0) {
09127       sprintf(str, "invalid rpc ID (%d)", routine_id);
09128       cm_msg(MERROR, "rpc_client_call", str);
09129       return RPC_INVALID_ID;
09130    }
09131 
09132    /* examine variable argument list and convert it to parameter array */
09133    va_start(ap, routine_id);
09134 
09135    /* find out if we are on a big endian system */
09136    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09137 
09138    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09139       tid = rpc_list[rpc_index].param[i].tid;
09140       flags = rpc_list[rpc_index].param[i].flags;
09141 
09142       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09143           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09144           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09145 
09146       if (bpointer)
09147          arg_type = TID_ARRAY;
09148       else
09149          arg_type = tid;
09150 
09151       /* floats are passed as doubles, at least under NT */
09152       if (tid == TID_FLOAT && !bpointer)
09153          arg_type = TID_DOUBLE;
09154 
09155       /* get pointer to argument */
09156       rpc_va_arg(&ap, arg_type, arg);
09157 
09158       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09159       if (bbig) {
09160          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09161             arg[0] = arg[3];
09162          }
09163          if (tid == TID_WORD || tid == TID_SHORT) {
09164             arg[0] = arg[2];
09165             arg[1] = arg[3];
09166          }
09167       }
09168 
09169       if (flags & RPC_IN) {
09170          if (bpointer)
09171             arg_size = tid_size[tid];
09172          else
09173             arg_size = tid_size[arg_type];
09174 
09175          /* for strings, the argument size depends on the string length */
09176          if (tid == TID_STRING || tid == TID_LINK)
09177             arg_size = 1 + strlen((char *) *((char **) arg));
09178 
09179          /* for varibale length arrays, the size is given by
09180             the next parameter on the stack */
09181          if (flags & RPC_VARARRAY) {
09182             memcpy(&aptmp, &ap, sizeof(ap));
09183             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09184 
09185             if (flags & RPC_OUT)
09186                arg_size = *((INT *) * ((void **) arg_tmp));
09187             else
09188                arg_size = *((INT *) arg_tmp);
09189 
09190             *((INT *) param_ptr) = ALIGN8(arg_size);
09191             param_ptr += ALIGN8(sizeof(INT));
09192          }
09193 
09194          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09195             arg_size = rpc_list[rpc_index].param[i].n;
09196 
09197          /* always align parameter size */
09198          param_size = ALIGN8(arg_size);
09199 
09200          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > (INT) NET_BUFFER_SIZE) {
09201             cm_msg(MERROR, "rpc_client_call",
09202                    "parameters (%d) too large for network buffer (%d)",
09203                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, NET_BUFFER_SIZE);
09204             return RPC_EXCEED_BUFFER;
09205          }
09206 
09207          if (bpointer)
09208             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09209          else {
09210             /* floats are passed as doubles on most systems */
09211             if (tid != TID_FLOAT)
09212                memcpy(param_ptr, arg, arg_size);
09213             else
09214                *((float *) param_ptr) = (float) *((double *) arg);
09215          }
09216 
09217          param_ptr += param_size;
09218 
09219       }
09220    }
09221 
09222    va_end(ap);
09223 
09224    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09225 
09226    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09227 
09228    /* in FAST TCP mode, only send call and return immediately */
09229    if (transport == RPC_FTCP) {
09230       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09231 
09232       if (i != send_size) {
09233          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09234          return RPC_NET_ERROR;
09235       }
09236 
09237       return RPC_SUCCESS;
09238    }
09239 
09240    /* in TCP mode, send and wait for reply on send socket */
09241    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09242    if (i != send_size) {
09243       cm_msg(MERROR, "rpc_client_call",
09244              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09245              rpc_list[rpc_index].name, _client_connection[idx].host_name);
09246       return RPC_NET_ERROR;
09247    }
09248 
09249    /* make some timeout checking */
09250    if (rpc_timeout > 0) {
09251       FD_ZERO(&readfds);
09252       FD_SET(send_sock, &readfds);
09253 
09254       timeout.tv_sec = rpc_timeout / 1000;
09255       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09256 
09257       do {
09258          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09259 
09260          /* if an alarm signal was cought, restart select with reduced timeout */
09261          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09262             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09263 
09264       } while (status == -1);   /* dont return if an alarm signal was cought */
09265 
09266       if (!FD_ISSET(send_sock, &readfds)) {
09267          cm_msg(MERROR, "rpc_client_call", "rpc timeout, routine = \"%s\", host = \"%s\"",
09268                 rpc_list[rpc_index].name, _client_connection[idx].host_name);
09269 
09270          /* disconnect to avoid that the reply to this rpc_call comes at
09271             the next rpc_call */
09272          rpc_client_disconnect(hConn, FALSE);
09273 
09274          return RPC_TIMEOUT;
09275       }
09276    }
09277 
09278    /* receive result on send socket */
09279    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09280 
09281    if (i <= 0) {
09282       cm_msg(MERROR, "rpc_client_call",
09283              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09284              rpc_list[rpc_index].name, _client_connection[idx].host_name);
09285       return RPC_NET_ERROR;
09286    }
09287 
09288    /* extract result variables and place it to argument list */
09289    status = nc->header.routine_id;
09290 
09291    va_start(ap, routine_id);
09292 
09293    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09294       tid = rpc_list[rpc_index].param[i].tid;
09295       flags = rpc_list[rpc_index].param[i].flags;
09296 
09297       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09298           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09299           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09300 
09301       if (bpointer)
09302          arg_type = TID_ARRAY;
09303       else
09304          arg_type = rpc_list[rpc_index].param[i].tid;
09305 
09306       if (tid == TID_FLOAT && !bpointer)
09307          arg_type = TID_DOUBLE;
09308 
09309       rpc_va_arg(&ap, arg_type, arg);
09310 
09311       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09312          tid = rpc_list[rpc_index].param[i].tid;
09313          flags = rpc_list[rpc_index].param[i].flags;
09314 
09315          arg_size = tid_size[tid];
09316 
09317          if (tid == TID_STRING || tid == TID_LINK)
09318             arg_size = strlen((char *) (param_ptr)) + 1;
09319 
09320          if (flags & RPC_VARARRAY) {
09321             arg_size = *((INT *) param_ptr);
09322             param_ptr += ALIGN8(sizeof(INT));
09323          }
09324 
09325          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09326             arg_size = rpc_list[rpc_index].param[i].n;
09327 
09328          /* return parameters are always pointers */
09329          if (*((char **) arg))
09330             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09331 
09332          /* parameter size is always aligned */
09333          param_size = ALIGN8(arg_size);
09334 
09335          param_ptr += param_size;
09336       }
09337    }
09338 
09339    va_end(ap);
09340 
09341    return status;
09342 }
09343 
09344 /* mutex for multi-threaded applications calling the RPC layer */
09345 int _mutex_rpc;
09346 
09347 /********************************************************************/
09348 INT rpc_call(const INT routine_id, ...)
09349 /********************************************************************\
09350 
09351   Routine: rpc_call
09352 
09353   Purpose: Call a function on a MIDAS server
09354 
09355   Input:
09356     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09357 
09358     ...                     variable argument list
09359 
09360   Output:
09361     (depends on argument list)
09362 
09363   Function value:
09364     RPC_SUCCESS             Successful completion
09365     RPC_NET_ERROR           Error in socket call
09366     RPC_NO_CONNECTION       No active connection
09367     RPC_TIMEOUT             Timeout in RPC call
09368     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09369     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09370 
09371 \********************************************************************/
09372 {
09373    va_list ap, aptmp;
09374    char arg[8], arg_tmp[8];
09375    INT arg_type, transport, rpc_timeout;
09376    INT i, idx, status;
09377    INT param_size, arg_size, send_size;
09378    INT tid, flags;
09379    fd_set readfds;
09380    struct timeval timeout;
09381    char *param_ptr, str[80];
09382    BOOL bpointer, bbig;
09383    NET_COMMAND *nc;
09384    int send_sock;
09385 
09386    send_sock = _server_connection.send_sock;
09387    transport = _server_connection.transport;
09388    rpc_timeout = _server_connection.rpc_timeout;
09389 
09390    /* init network buffer */
09391    if (_net_send_buffer_size == 0) {
09392       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09393       if (_net_send_buffer == NULL) {
09394          cm_msg(MERROR, "rpc_call", "not enough memory to allocate network buffer");
09395          return RPC_EXCEED_BUFFER;
09396       }
09397       _net_send_buffer_size = NET_BUFFER_SIZE;
09398 
09399       /* create mutex for multi-threaded applications */
09400       ss_mutex_create("RPC", &_mutex_rpc);
09401    }
09402 
09403    status = ss_mutex_wait_for(_mutex_rpc, 10000);
09404    if (status != SS_SUCCESS) {
09405       return RPC_MUTEX_TIMEOUT;
09406    }
09407 
09408    nc = (NET_COMMAND *) _net_send_buffer;
09409    nc->header.routine_id = routine_id;
09410 
09411    if (transport == RPC_FTCP)
09412       nc->header.routine_id |= TCP_FAST;
09413 
09414    for (i = 0;; i++)
09415       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09416          break;
09417    idx = i;
09418    if (rpc_list[i].id == 0) {
09419       sprintf(str, "invalid rpc ID (%d)", routine_id);
09420       cm_msg(MERROR, "rpc_call", str);
09421       return RPC_INVALID_ID;
09422    }
09423 
09424    /* examine variable argument list and convert it to parameter array */
09425    va_start(ap, routine_id);
09426 
09427    /* find out if we are on a big endian system */
09428    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09429 
09430    for (i = 0, param_ptr = nc->param; rpc_list[idx].param[i].tid != 0; i++) {
09431       tid = rpc_list[idx].param[i].tid;
09432       flags = rpc_list[idx].param[i].flags;
09433 
09434       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09435           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09436           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09437 
09438       if (bpointer)
09439          arg_type = TID_ARRAY;
09440       else
09441          arg_type = tid;
09442 
09443       /* floats are passed as doubles, at least under NT */
09444       if (tid == TID_FLOAT && !bpointer)
09445          arg_type = TID_DOUBLE;
09446 
09447       /* get pointer to argument */
09448       rpc_va_arg(&ap, arg_type, arg);
09449 
09450       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09451       if (bbig) {
09452          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09453             arg[0] = arg[3];
09454          }
09455          if (tid == TID_WORD || tid == TID_SHORT) {
09456             arg[0] = arg[2];
09457             arg[1] = arg[3];
09458          }
09459       }
09460 
09461       if (flags & RPC_IN) {
09462          if (bpointer)
09463             arg_size = tid_size[tid];
09464          else
09465             arg_size = tid_size[arg_type];
09466 
09467          /* for strings, the argument size depends on the string length */
09468          if (tid == TID_STRING || tid == TID_LINK)
09469             arg_size = 1 + strlen((char *) *((char **) arg));
09470 
09471          /* for varibale length arrays, the size is given by
09472             the next parameter on the stack */
09473          if (flags & RPC_VARARRAY) {
09474             memcpy(&aptmp, &ap, sizeof(ap));
09475             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09476 
09477             if (flags & RPC_OUT)
09478                arg_size = *((INT *) * ((void **) arg_tmp));
09479             else
09480                arg_size = *((INT *) arg_tmp);
09481 
09482             *((INT *) param_ptr) = ALIGN8(arg_size);
09483             param_ptr += ALIGN8(sizeof(INT));
09484          }
09485 
09486          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09487             arg_size = rpc_list[idx].param[i].n;
09488 
09489          /* always align parameter size */
09490          param_size = ALIGN8(arg_size);
09491 
09492          if ((POINTER_T) param_ptr - (POINTER_T) nc + param_size > (INT) NET_BUFFER_SIZE) {
09493             cm_msg(MERROR, "rpc_call",
09494                    "parameters (%d) too large for network buffer (%d)",
09495                    (POINTER_T) param_ptr - (POINTER_T) nc + param_size, NET_BUFFER_SIZE);
09496             ss_mutex_release(_mutex_rpc);
09497             return RPC_EXCEED_BUFFER;
09498          }
09499 
09500          if (bpointer)
09501             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09502          else {
09503             /* floats are passed as doubles on most systems */
09504             if (tid != TID_FLOAT)
09505                memcpy(param_ptr, arg, arg_size);
09506             else
09507                *((float *) param_ptr) = (float) *((double *) arg);
09508          }
09509 
09510          param_ptr += param_size;
09511 
09512       }
09513    }
09514 
09515    va_end(ap);
09516 
09517    nc->header.param_size = (POINTER_T) param_ptr - (POINTER_T) nc->param;
09518 
09519    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09520 
09521    /* in FAST TCP mode, only send call and return immediately */
09522    if (transport == RPC_FTCP) {
09523       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09524 
09525       if (i != send_size) {
09526          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09527          ss_mutex_release(_mutex_rpc);
09528          return RPC_NET_ERROR;
09529       }
09530 
09531       ss_mutex_release(_mutex_rpc);
09532       return RPC_SUCCESS;
09533    }
09534 
09535    /* in TCP mode, send and wait for reply on send socket */
09536    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09537    if (i != send_size) {
09538       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09539       ss_mutex_release(_mutex_rpc);
09540       return RPC_NET_ERROR;
09541    }
09542 
09543    /* make some timeout checking */
09544    if (rpc_timeout > 0) {
09545       FD_ZERO(&readfds);
09546       FD_SET(send_sock, &readfds);
09547 
09548       timeout.tv_sec = rpc_timeout / 1000;
09549       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09550 
09551       do {
09552          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09553 
09554          /* if an alarm signal was cought, restart select with reduced timeout */
09555          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09556             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09557 
09558       } while (status == -1);   /* dont return if an alarm signal was cought */
09559 
09560       if (!FD_ISSET(send_sock, &readfds)) {
09561          cm_msg(MERROR, "rpc_call", "rpc timeout, routine = \"%s\"", rpc_list[idx].name);
09562 
09563          /* disconnect to avoid that the reply to this rpc_call comes at
09564             the next rpc_call */
09565          rpc_server_disconnect();
09566 
09567          ss_mutex_release(_mutex_rpc);
09568          return RPC_TIMEOUT;
09569       }
09570    }
09571 
09572    /* receive result on send socket */
09573    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09574 
09575    if (i <= 0) {
09576       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"", rpc_list[idx].name);
09577       ss_mutex_release(_mutex_rpc);
09578       return RPC_NET_ERROR;
09579    }
09580 
09581    /* extract result variables and place it to argument list */
09582    status = nc->header.routine_id;
09583 
09584    va_start(ap, routine_id);
09585 
09586    for (i = 0, param_ptr = nc->param; rpc_list[idx].param[i].tid != 0; i++) {
09587       tid = rpc_list[idx].param[i].tid;
09588       flags = rpc_list[idx].param[i].flags;
09589 
09590       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09591           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09592           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09593 
09594       if (bpointer)
09595          arg_type = TID_ARRAY;
09596       else
09597          arg_type = rpc_list[idx].param[i].tid;
09598 
09599       if (tid == TID_FLOAT && !bpointer)
09600          arg_type = TID_DOUBLE;
09601 
09602       rpc_va_arg(&ap, arg_type, arg);
09603 
09604       if (rpc_list[idx].param[i].flags & RPC_OUT) {
09605          tid = rpc_list[idx].param[i].tid;
09606          arg_size = tid_size[tid];
09607 
09608          if (tid == TID_STRING || tid == TID_LINK)
09609             arg_size = strlen((char *) (param_ptr)) + 1;
09610 
09611          if (flags & RPC_VARARRAY) {
09612             arg_size = *((INT *) param_ptr);
09613             param_ptr += ALIGN8(sizeof(INT));
09614          }
09615 
09616          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09617             arg_size = rpc_list[idx].param[i].n;
09618 
09619          /* return parameters are always pointers */
09620          if (*((char **) arg))
09621             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09622 
09623          /* parameter size is always aligned */
09624          param_size = ALIGN8(arg_size);
09625 
09626          param_ptr += param_size;
09627       }
09628    }
09629 
09630    va_end(ap);
09631 
09632    ss_mutex_release(_mutex_rpc);
09633 
09634    return status;
09635 }
09636 
09637 
09638 /********************************************************************/
09639 INT rpc_set_opt_tcp_size(INT tcp_size)
09640 {
09641    INT old;
09642 
09643    old = _opt_tcp_size;
09644    _opt_tcp_size = tcp_size;
09645    return old;
09646 }
09647 
09648 INT rpc_get_opt_tcp_size()
09649 {
09650    return _opt_tcp_size;
09651 }
09652 
09653 /**dox***************************************************************/
09654 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09655 
09656 /********************************************************************/
09657 /**
09658 Fast send_event routine which bypasses the RPC layer and
09659            sends the event directly at the TCP level.
09660 @param buffer_handle      Handle of the buffer to send the event to.
09661                           Must be obtained via bm_open_buffer.
09662 @param source             Address of the event to send. It must have
09663                           a proper event header.
09664 @param buf_size           Size of event in bytes with header.
09665 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
09666                           function returns immediately if it cannot
09667                           send the event over the network. In SYNC
09668                           mode, it waits until the packet is sent
09669                           (blocking).
09670 @param mode               Determines in which mode the event is sent.
09671                           If zero, use RPC socket, if one, use special
09672                           event socket to bypass RPC layer on the
09673                           server side.
09674 
09675 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR,
09676         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER
09677 */
09678 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag, INT mode)
09679 {
09680    INT i;
09681    NET_COMMAND *nc;
09682    unsigned long flag;
09683    BOOL would_block = 0;
09684    DWORD aligned_buf_size;
09685 
09686    aligned_buf_size = ALIGN8(buf_size);
09687    _rpc_sock = mode == 0 ? _server_connection.send_sock : _server_connection.event_sock;
09688 
09689    if ((INT)aligned_buf_size != (INT) (ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER)))) {
09690       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
09691       return BM_INVALID_PARAM;
09692    }
09693    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
09694       cm_msg(MERROR, "rpc_send_event",
09695              "event size (%d) larger than maximum event size (%d)",
09696              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
09697       return RPC_EXCEED_BUFFER;
09698    }
09699 
09700    if (!rpc_is_remote())
09701       return bm_send_event(buffer_handle, source, buf_size, async_flag);
09702 
09703    /* init network buffer */
09704    if (!_tcp_buffer)
09705       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
09706    if (!_tcp_buffer) {
09707       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
09708       return RPC_EXCEED_BUFFER;
09709    }
09710 
09711    /* check if not enough space in TCP buffer */
09712    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
09713        (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
09714       /* set socket to nonblocking IO */
09715       if (async_flag == ASYNC) {
09716          flag = 1;
09717 #ifdef OS_VXWORKS
09718          ioctlsocket(_rpc_sock, FIONBIO, (int) &flag);
09719 #else
09720          ioctlsocket(_rpc_sock, FIONBIO, &flag);
09721 #endif
09722       }
09723 
09724       i = send_tcp(_rpc_sock, _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09725 
09726       if (i < 0)
09727 #ifdef OS_WINNT
09728          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
09729 #else
09730          would_block = (errno == EWOULDBLOCK);
09731 #endif
09732 
09733       /* set socket back to blocking IO */
09734       if (async_flag == ASYNC) {
09735          flag = 0;
09736 #ifdef OS_VXWORKS
09737          ioctlsocket(_rpc_sock, FIONBIO, (int) &flag);
09738 #else
09739          ioctlsocket(_rpc_sock, FIONBIO, &flag);
09740 #endif
09741       }
09742 
09743       /* increment read pointer */
09744       if (i > 0)
09745          _tcp_rp += i;
09746 
09747       /* check if whole buffer is sent */
09748       if (_tcp_rp == _tcp_wp)
09749          _tcp_rp = _tcp_wp = 0;
09750 
09751       if (i < 0 && !would_block) {
09752          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed, return code = %d", i);
09753          return RPC_NET_ERROR;
09754       }
09755 
09756       /* return if buffer is not emptied */
09757       if (_tcp_wp > 0)
09758          return BM_ASYNC_RETURN;
09759    }
09760 
09761    if (mode == 0) {
09762       nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
09763       nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
09764       nc->header.param_size = 4 * 8 + aligned_buf_size;
09765 
09766       /* assemble parameters manually */
09767       *((INT *) (&nc->param[0])) = buffer_handle;
09768       *((INT *) (&nc->param[8])) = buf_size;
09769 
09770       /* send events larger than optimal buffer size directly */
09771       if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
09772          /* send header */
09773          send_tcp(_rpc_sock, _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
09774 
09775          /* send data */
09776          send_tcp(_rpc_sock, (char *) source, aligned_buf_size, 0);
09777 
09778          /* send last two parameters */
09779          *((INT *) (&nc->param[0])) = buf_size;
09780          *((INT *) (&nc->param[8])) = 0;
09781          send_tcp(_rpc_sock, &nc->param[0], 16, 0);
09782       } else {
09783          /* copy event */
09784          memcpy(&nc->param[16], source, buf_size);
09785 
09786          /* last two parameters (buf_size and async_flag */
09787          *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
09788          *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
09789 
09790          _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09791       }
09792 
09793    } else {
09794 
09795       /* send events larger than optimal buffer size directly */
09796       if (aligned_buf_size + 4 * 8 + sizeof(INT) >= (DWORD) _opt_tcp_size) {
09797          /* send buffer */
09798          send_tcp(_rpc_sock, (char *) &buffer_handle, sizeof(INT), 0);
09799 
09800          /* send data */
09801          send_tcp(_rpc_sock, (char *) source, aligned_buf_size, 0);
09802       } else {
09803          /* copy event */
09804          *((INT *) (_tcp_buffer + _tcp_wp)) = buffer_handle;
09805          _tcp_wp += sizeof(INT);
09806          memcpy(_tcp_buffer + _tcp_wp, source, buf_size);
09807 
09808          _tcp_wp += aligned_buf_size;
09809       }
09810    }
09811 
09812    return RPC_SUCCESS;
09813 }
09814 
09815 
09816 /**dox***************************************************************/
09817 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09818 
09819 /********************************************************************/
09820 int rpc_get_send_sock()
09821 /********************************************************************\
09822 
09823   Routine: rpc_get_send_sock
09824 
09825   Purpose: Return send socket to MIDAS server. Used by MFE.C for
09826            optimized event sending.
09827 
09828   Input:
09829     none
09830 
09831   Output:
09832     none
09833 
09834   Function value:
09835     int    socket
09836 
09837 \********************************************************************/
09838 {
09839    return _server_connection.send_sock;
09840 }
09841 
09842 
09843 /********************************************************************/
09844 int rpc_get_event_sock()
09845 /********************************************************************\
09846 
09847   Routine: rpc_get_event_sock
09848 
09849   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
09850            optimized event sending.
09851 
09852   Input:
09853     none
09854 
09855   Output:
09856     none
09857 
09858   Function value:
09859     int    socket
09860 
09861 \********************************************************************/
09862 {
09863    return _server_connection.event_sock;
09864 }
09865 
09866 /**dox***************************************************************/
09867 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09868 
09869 /********************************************************************/
09870 /**
09871 Send event residing in the TCP cache buffer filled by
09872            rpc_send_event. This routine should be called when a
09873            run is stopped.
09874 
09875 @return RPC_SUCCESS, RPC_NET_ERROR
09876 */
09877 INT rpc_flush_event()
09878 {
09879    INT i;
09880 
09881    if (!rpc_is_remote())
09882       return RPC_SUCCESS;
09883 
09884    /* return if rpc_send_event was not called */
09885    if (!_tcp_buffer || _tcp_wp == 0)
09886       return RPC_SUCCESS;
09887 
09888    /* empty TCP buffer */
09889    if (_tcp_wp > 0) {
09890       i = send_tcp(_rpc_sock, _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09891 
09892       if (i != _tcp_wp - _tcp_rp) {
09893          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
09894          return RPC_NET_ERROR;
09895       }
09896    }
09897 
09898    _tcp_rp = _tcp_wp = 0;
09899 
09900    return RPC_SUCCESS;
09901 }
09902 
09903 /**dox***************************************************************/
09904 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09905 
09906 /********************************************************************/
09907 
09908 typedef struct {
09909    int transition;
09910    int run_number;
09911    time_t trans_time;
09912    int sequence_number;
09913 } TR_FIFO;
09914 
09915 static TR_FIFO tr_fifo[10];
09916 static int trf_wp, trf_rp;
09917 
09918 static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
09919 /********************************************************************\
09920 
09921   Routine: rpc_transition_dispatch
09922 
09923   Purpose: Gets called when a transition function was registered and
09924            a transition occured. Internal use only.
09925 
09926   Input:
09927     INT    idx              RPC function ID
09928     void   *prpc_param      RPC parameters
09929 
09930   Output:
09931     none
09932 
09933   Function value:
09934     INT    return value from called user routine
09935 
09936 \********************************************************************/
09937 {
09938    INT status, i;
09939 
09940    /* erase error string */
09941    *(CSTRING(2)) = 0;
09942 
09943    if (idx == RPC_RC_TRANSITION) {
09944       for (i = 0; i < MAX_TRANSITIONS; i++)
09945          if (_trans_table[i].transition == CINT(0) && _trans_table[i].sequence_number == CINT(4))
09946             break;
09947 
09948       /* call registerd function */
09949       if (i < MAX_TRANSITIONS) {
09950          if (_trans_table[i].func)
09951             /* execute callback if defined */
09952             status = _trans_table[i].func(CINT(1), CSTRING(2));
09953          else {
09954             /* store transition in FIFO */
09955             tr_fifo[trf_wp].transition = CINT(0);
09956             tr_fifo[trf_wp].run_number = CINT(1);
09957             tr_fifo[trf_wp].trans_time = time(NULL);
09958             tr_fifo[trf_wp].sequence_number = CINT(4);
09959             trf_wp = (trf_wp + 1) % 10;
09960             status = RPC_SUCCESS;
09961          }
09962       } else
09963          status = RPC_SUCCESS;
09964 
09965    } else {
09966       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
09967       status = RPC_INVALID_ID;
09968    }
09969 
09970    return status;
09971 }
09972 
09973 /********************************************************************/
09974 int cm_query_transition(int *transition, int *run_number, int *trans_time)
09975 /********************************************************************\
09976 
09977   Routine: cm_query_transition
09978 
09979   Purpose: Query system if transition has occured. Normally, one
09980            registers callbacks for transitions via
09981            cm_register_transition. In some environments however,
09982            callbacks are not possible. In that case one spciefies
09983            a NULL pointer as the callback routine and can query
09984            transitions "manually" by calling this functions. A small
09985            FIFO takes care that no transition is lost if this functions
09986            did not get called between some transitions.
09987 
09988   Output:
09989     INT   *transition        Type of transition, one of TR_xxx
09990     INT   *run_nuber         Run number for transition
09991     time_t *trans_time       Time (in UNIX time) of transition
09992 
09993   Function value:
09994     FALSE  No transition occured since last call
09995     TRUE   Transition occured
09996 
09997 \********************************************************************/
09998 {
09999 
10000    if (trf_wp == trf_rp)
10001       return FALSE;
10002 
10003    if (transition)
10004       *transition = tr_fifo[trf_rp].transition;
10005 
10006    if (run_number)
10007       *run_number = tr_fifo[trf_rp].run_number;
10008 
10009    if (trans_time)
10010       *trans_time = (int) tr_fifo[trf_rp].trans_time;
10011 
10012    trf_rp = (trf_rp + 1) % 10;
10013 
10014    return TRUE;
10015 }
10016 
10017 /********************************************************************\
10018 *                        server functions                            *
10019 \********************************************************************/
10020 
10021 #if 0
10022 void debug_dump(unsigned char *p, int size)
10023 {
10024    int i, j;
10025    unsigned char c;
10026 
10027    for (i = 0; i < (size-1)/16+1; i++) {
10028       printf("%p ", p + i * 16);
10029       for (j = 0; j < 16; j++)
10030          if (i * 16 + j < size)
10031             printf("%02X ", p[i * 16 + j]);
10032          else
10033             printf("   ");
10034       printf(" ");
10035 
10036       for (j = 0; j < 16; j++) {
10037          c = p[i * 16 + j];
10038          if (i * 16 + j < size)
10039             printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
10040       }
10041       printf("\n");
10042    }
10043 
10044    printf("\n");
10045 }
10046 #endif
10047 
10048 /********************************************************************/
10049 INT recv_tcp_server(INT idx, char *buffer, DWORD buffer_size, INT flags, INT * remaining)
10050 /********************************************************************\
10051 
10052   Routine: recv_tcp_server
10053 
10054   Purpose: TCP receive routine with local cache. To speed up network
10055            performance, a 64k buffer is read in at once and split into
10056            several RPC command on successive calls to recv_tcp_server.
10057            Therefore, the number of recv() calls is minimized.
10058 
10059            This routine is ment to be called by the server process.
10060            Clients should call recv_tcp instead.
10061 
10062   Input:
10063     INT   idx                Index of server connection
10064     DWORD buffer_size        Size of the buffer in bytes.
10065     INT   flags              Flags passed to recv()
10066     INT   convert_flags      Convert flags needed for big/little
10067                              endian conversion
10068 
10069   Output:
10070     char  *buffer            Network receive buffer.
10071     INT   *remaining         Remaining data in cache
10072 
10073   Function value:
10074     INT                      Same as recv()
10075 
10076 \********************************************************************/
10077 {
10078    INT size, param_size;
10079    NET_COMMAND *nc;
10080    INT write_ptr, read_ptr, misalign;
10081    char *net_buffer;
10082    INT copied, status;
10083    INT sock;
10084 
10085    sock = _server_acception[idx].recv_sock;
10086 
10087    if (flags & MSG_PEEK) {
10088       status = recv(sock, buffer, buffer_size, flags);
10089       if (status == -1)
10090          cm_msg(MERROR, "recv_tcp_server",
10091                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status, errno, strerror(errno));
10092       return status;
10093    }
10094 
10095    if (!_server_acception[idx].net_buffer) {
10096       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10097          _server_acception[idx].net_buffer_size = NET_TCP_SIZE;
10098       else
10099          _server_acception[idx].net_buffer_size = NET_BUFFER_SIZE;
10100 
10101       _server_acception[idx].net_buffer = (char *) M_MALLOC(_server_acception[idx].net_buffer_size);
10102       _server_acception[idx].write_ptr = 0;
10103       _server_acception[idx].read_ptr = 0;
10104       _server_acception[idx].misalign = 0;
10105    }
10106    if (!_server_acception[idx].net_buffer) {
10107       cm_msg(MERROR, "recv_tcp_server", "not enough memory to allocate network buffer");
10108       return -1;
10109    }
10110 
10111    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
10112       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10113       return -1;
10114    }
10115 
10116    copied = 0;
10117    param_size = -1;
10118 
10119    write_ptr = _server_acception[idx].write_ptr;
10120    read_ptr = _server_acception[idx].read_ptr;
10121    misalign = _server_acception[idx].misalign;
10122    net_buffer = _server_acception[idx].net_buffer;
10123 
10124    do {
10125       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
10126          if (param_size == -1) {
10127             if (copied > 0) {
10128                /* assemble split header */
10129                memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
10130                nc = (NET_COMMAND *) (buffer);
10131             } else
10132                nc = (NET_COMMAND *) (net_buffer + read_ptr);
10133 
10134             param_size = (INT) nc->header.param_size;
10135 
10136             if (_server_acception[idx].convert_flags)
10137                rpc_convert_single(&param_size, TID_DWORD, 0, _server_acception[idx].convert_flags);
10138          }
10139 
10140          /* check if parameters fit in buffer */
10141          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
10142             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10143             _server_acception[idx].read_ptr = _server_acception[idx].write_ptr = 0;
10144             return -1;
10145          }
10146 
10147          /* check if we have all parameters in buffer */
10148          if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
10149             break;
10150       }
10151 
10152       /* not enough data, so copy partially and get new */
10153       size = write_ptr - read_ptr;
10154 
10155       if (size > 0) {
10156          memcpy(buffer + copied, net_buffer + read_ptr, size);
10157          copied += size;
10158          read_ptr = write_ptr;
10159       }
10160 #ifdef OS_UNIX
10161       do {
10162          write_ptr = recv(sock, net_buffer + misalign, _server_acception[idx].net_buffer_size - 8, flags);
10163 
10164          /* don't return if an alarm signal was cought */
10165       } while (write_ptr == -1 && errno == EINTR);
10166 #else
10167       write_ptr = recv(sock, net_buffer + misalign, _server_acception[idx].net_buffer_size - 8, flags);
10168 #endif
10169 
10170       /* abort if connection broken */
10171       if (write_ptr <= 0) {
10172          if (write_ptr == 0)
10173             cm_msg(MERROR, "recv_tcp_server", "rpc connection from \'%s\' on \'%s\' unexpectedly closed",
10174                    _server_acception[idx].prog_name, _server_acception[idx].host_name);
10175          else
10176             cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)",
10177                    write_ptr, errno, strerror(errno));
10178 
10179          if (remaining)
10180             *remaining = 0;
10181 
10182          return write_ptr;
10183       }
10184 
10185       read_ptr = misalign;
10186       write_ptr += misalign;
10187 
10188       misalign = write_ptr % 8;
10189    } while (TRUE);
10190 
10191    /* copy rest of parameters */
10192    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
10193    memcpy(buffer + copied, net_buffer + read_ptr, size);
10194    read_ptr += size;
10195 
10196    if (remaining) {
10197       /* don't keep rpc_server_receive in an infinite loop */
10198       if (write_ptr - read_ptr < param_size)
10199          *remaining = 0;
10200       else
10201          *remaining = write_ptr - read_ptr;
10202    }
10203 
10204    _server_acception[idx].write_ptr = write_ptr;
10205    _server_acception[idx].read_ptr = read_ptr;
10206    _server_acception[idx].misalign = misalign;
10207 
10208    return size + copied;
10209 }
10210 
10211 
10212 /********************************************************************/
10213 INT recv_tcp_check(int sock)
10214 /********************************************************************\
10215 
10216   Routine: recv_tcp_check
10217 
10218   Purpose: Check if in TCP receive buffer associated with sock is
10219            some data. Called by ss_suspend.
10220 
10221   Input:
10222     INT   sock               TCP receive socket
10223 
10224   Output:
10225     none
10226 
10227   Function value:
10228     INT   count              Number of bytes remaining in TCP buffer
10229 
10230 \********************************************************************/
10231 {
10232    INT idx;
10233 
10234    /* figure out to which connection socket belongs */
10235    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
10236       if (_server_acception[idx].recv_sock == sock)
10237          break;
10238 
10239    return _server_acception[idx].write_ptr - _server_acception[idx].read_ptr;
10240 }
10241 
10242 
10243 /********************************************************************/
10244 INT recv_event_server(INT idx, char *buffer, DWORD buffer_size, INT flags, INT * remaining)
10245 /********************************************************************\
10246 
10247   Routine: recv_event_server
10248 
10249   Purpose: TCP event receive routine with local cache. To speed up
10250            network performance, a 64k buffer is read in at once and
10251            split into several RPC command on successive calls to
10252            recv_event_server. Therefore, the number of recv() calls
10253            is minimized.
10254 
10255            This routine is ment to be called by the server process.
10256            Clients should call recv_tcp instead.
10257 
10258   Input:
10259     INT   idx                Index of server connection
10260     DWORD buffer_size        Size of the buffer in bytes.
10261     INT   flags              Flags passed to recv()
10262     INT   convert_flags      Convert flags needed for big/little
10263                              endian conversion
10264 
10265   Output:
10266     char  *buffer            Network receive buffer.
10267     INT   *remaining         Remaining data in cache
10268 
10269   Function value:
10270     INT                      Same as recv()
10271 
10272 \********************************************************************/
10273 {
10274    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10275    EVENT_HEADER *pevent;
10276    INT write_ptr, read_ptr, misalign;
10277    char *net_buffer;
10278    INT copied, status;
10279    INT sock;
10280    RPC_SERVER_ACCEPTION *psa;
10281 
10282    psa = &_server_acception[idx];
10283    sock = psa->event_sock;
10284 
10285    if (flags & MSG_PEEK) {
10286       status = recv(sock, buffer, buffer_size, flags);
10287       if (status == -1)
10288          cm_msg(MERROR, "recv_event_server",
10289                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status, errno, strerror(errno));
10290       return status;
10291    }
10292 
10293    if (!psa->ev_net_buffer) {
10294       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10295          psa->net_buffer_size = NET_TCP_SIZE;
10296       else
10297          psa->net_buffer_size = NET_BUFFER_SIZE;
10298 
10299       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10300       psa->ev_write_ptr = 0;
10301       psa->ev_read_ptr = 0;
10302       psa->ev_misalign = 0;
10303    }
10304    if (!psa->ev_net_buffer) {
10305       cm_msg(MERROR, "recv_event_server", "not enough memory to allocate network buffer");
10306       return -1;
10307    }
10308 
10309    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10310 
10311    if ((INT) buffer_size < header_size) {
10312       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10313       return -1;
10314    }
10315 
10316    copied = 0;
10317    event_size = -1;
10318 
10319    write_ptr = psa->ev_write_ptr;
10320    read_ptr = psa->ev_read_ptr;
10321    misalign = psa->ev_misalign;
10322    net_buffer = psa->ev_net_buffer;
10323 
10324    do {
10325       if (write_ptr - read_ptr >= header_size - copied) {
10326          if (event_size == -1) {
10327             if (copied > 0) {
10328                /* assemble split header */
10329                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10330                pbh = (INT *) buffer;
10331             } else
10332                pbh = (INT *) (net_buffer + read_ptr);
10333 
10334             pevent = (EVENT_HEADER *) (pbh + 1);
10335 
10336             event_size = pevent->data_size;
10337             if (psa->convert_flags)
10338                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10339 
10340             aligned_event_size = ALIGN8(event_size);
10341          }
10342 
10343          /* check if data part fits in buffer */
10344          if ((INT) buffer_size < aligned_event_size + header_size) {
10345             cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10346             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10347             return -1;
10348          }
10349 
10350          /* check if we have whole event in buffer */
10351          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10352             break;
10353       }
10354 
10355       /* not enough data, so copy partially and get new */
10356       size = write_ptr - read_ptr;
10357 
10358       if (size > 0) {
10359          memcpy(buffer + copied, net_buffer + read_ptr, size);
10360          copied += size;
10361          read_ptr = write_ptr;
10362       }
10363 #ifdef OS_UNIX
10364       do {
10365          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10366 
10367          /* don't return if an alarm signal was cought */
10368       } while (write_ptr == -1 && errno == EINTR);
10369 #else
10370       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10371 #endif
10372 
10373       /* abort if connection broken */
10374       if (write_ptr <= 0) {
10375          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)",
10376                 write_ptr, errno, strerror(errno));
10377 
10378          if (remaining)
10379             *remaining = 0;
10380 
10381          return write_ptr;
10382       }
10383 
10384       read_ptr = misalign;
10385       write_ptr += misalign;
10386 
10387       misalign = write_ptr % 8;
10388    } while (TRUE);
10389 
10390    /* copy rest of event */
10391    size = aligned_event_size + header_size - copied;
10392    if (size > 0) {
10393       memcpy(buffer + copied, net_buffer + read_ptr, size);
10394       read_ptr += size;
10395    }
10396 
10397    if (remaining)
10398       *remaining = write_ptr - read_ptr;
10399 
10400    psa->ev_write_ptr = write_ptr;
10401    psa->ev_read_ptr = read_ptr;
10402    psa->ev_misalign = misalign;
10403 
10404    /* convert header little endian/big endian */
10405    if (psa->convert_flags) {
10406       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10407 
10408       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10409       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10410       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10411       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10412       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10413       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10414    }
10415 
10416    return header_size + event_size;
10417 }
10418 
10419 
10420 /********************************************************************/
10421 INT recv_event_check(int sock)
10422 /********************************************************************\
10423 
10424   Routine: recv_event_check
10425 
10426   Purpose: Check if in TCP event receive buffer associated with sock
10427            is some data. Called by ss_suspend.
10428 
10429   Input:
10430     INT   sock               TCP receive socket
10431 
10432   Output:
10433     none
10434 
10435   Function value:
10436     INT   count              Number of bytes remaining in TCP buffer
10437 
10438 \********************************************************************/
10439 {
10440    INT idx;
10441 
10442    /* figure out to which connection socket belongs */
10443    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
10444       if (_server_acception[idx].event_sock == sock)
10445          break;
10446 
10447    return _server_acception[idx].ev_write_ptr - _server_acception[idx].ev_read_ptr;
10448 }
10449 
10450 
10451 /********************************************************************/
10452 INT rpc_register_server(INT server_type, char *name, INT * port, INT(*func) (INT, void **))
10453 /********************************************************************\
10454 
10455   Routine: rpc_register_server
10456 
10457   Purpose: Register the calling process as a MIDAS RPC server. Note
10458            that cm_connnect_experiment must be called prior to any call of
10459            rpc_register_server.
10460 
10461   Input:
10462     INT   server_type       One of the following constants:
10463                             ST_SINGLE: register a single process server
10464                             ST_MTHREAD: for each connection, start
10465                                         a new thread to serve it
10466                             ST_MPROCESS: for each connection, start
10467                                          a new process to server it
10468                             ST_SUBPROCESS: the routine was called from
10469                                            a multi process server
10470                             ST_REMOTE: register a client program server
10471                                        connected to the ODB
10472     char  *name             Name of .EXE file to start in MPROCESS mode
10473     INT   *port             TCP port for listen. NULL if listen as main
10474                             server (MIDAS_TCP_PORT is then used). If *port=0,
10475                             the OS chooses a free port and returns it. If
10476                             *port != 0, this port is used.
10477     INT   *func             Default dispatch function
10478 
10479   Output:
10480     INT   *port             Port under which server is listening.
10481 
10482   Function value:
10483     RPC_SUCCESS             Successful completion
10484     RPC_NET_ERROR           Error in socket call
10485     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10486 
10487 \********************************************************************/
10488 {
10489    struct sockaddr_in bind_addr;
10490    INT status, flag;
10491    unsigned int size;
10492 
10493 #ifdef OS_WINNT
10494    {
10495       WSADATA WSAData;
10496 
10497       /* Start windows sockets */
10498       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10499          return RPC_NET_ERROR;
10500    }
10501 #endif
10502 
10503    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10504 
10505    /* register system functions */
10506    rpc_register_functions(rpc_get_internal_list(0), func);
10507 
10508    if (name != NULL)
10509       rpc_set_server_option(RPC_OSERVER_NAME, (POINTER_T) name);
10510 
10511    /* in subprocess mode, don't start listener */
10512    if (server_type == ST_SUBPROCESS)
10513       return RPC_SUCCESS;
10514 
10515    /* create a socket for listening */
10516    _lsock = socket(AF_INET, SOCK_STREAM, 0);
10517    if (_lsock == -1) {
10518       cm_msg(MERROR, "rpc_register_server", "socket(AF_INET, SOCK_STREAM) failed, errno %d (%s)", errno, strerror(errno));
10519       return RPC_NET_ERROR;
10520    }
10521 
10522    /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
10523 #if defined(F_SETFD) && defined(FD_CLOEXEC)
10524    status = fcntl(_lsock, F_SETFD, fcntl(_lsock, F_GETFD) | FD_CLOEXEC);
10525    if (status < 0) {
10526       cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
10527       return RPC_NET_ERROR;
10528    }
10529 #endif
10530 
10531    /* reuse address, needed if previous server stopped (30s timeout!) */
10532    flag = 1;
10533    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
10534    if (status < 0) {
10535       cm_msg(MERROR, "rpc_register_server", "setsockopt(SO_REUSEADDR) failed, errno %d (%s)", errno, strerror(errno));
10536       return RPC_NET_ERROR;
10537    }
10538 
10539    /* bind local node name and port to socket */
10540    memset(&bind_addr, 0, sizeof(bind_addr));
10541    bind_addr.sin_family = AF_INET;
10542    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
10543 
10544    if (!port)
10545       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
10546    else
10547       bind_addr.sin_port = htons((short) (*port));
10548 
10549    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
10550    if (status < 0) {
10551       cm_msg(MERROR, "rpc_register_server", "bind() failed, errno %d (%s)", errno, strerror(errno));
10552       return RPC_NET_ERROR;
10553    }
10554 
10555    /* listen for connection */
10556 #ifdef OS_MSDOS
10557    status = listen(_lsock, 1);
10558 #else
10559    status = listen(_lsock, SOMAXCONN);
10560 #endif
10561    if (status < 0) {
10562       cm_msg(MERROR, "rpc_register_server", "listen() failed, errno %d (%s)", errno, strerror(errno));
10563       return RPC_NET_ERROR;
10564    }
10565 
10566    /* return port wich OS has choosen */
10567    if (port && *port == 0) {
10568       size = sizeof(bind_addr);
10569       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
10570       *port = ntohs(bind_addr.sin_port);
10571    }
10572 
10573    /* define callbacks for ss_suspend */
10574    if (server_type == ST_REMOTE)
10575       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
10576    else
10577       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
10578 
10579    return RPC_SUCCESS;
10580 }
10581 
10582 typedef struct {
10583    int  tid;
10584    char *buffer;
10585 } TLS_POINTER;
10586 
10587 TLS_POINTER *tls_buffer = NULL;
10588 int tls_size = 0;
10589 
10590 /********************************************************************/
10591 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
10592 /********************************************************************\
10593 
10594   Routine: rpc_execute
10595 
10596   Purpose: Execute a RPC command received over the network
10597 
10598   Input:
10599     INT  sock               TCP socket to which the result should be
10600                             send back
10601 
10602     char *buffer            Command buffer
10603     INT  convert_flags      Flags for data conversion
10604 
10605   Output:
10606     none
10607 
10608   Function value:
10609     RPC_SUCCESS             Successful completion
10610     RPC_INVALID_ID          Invalid routine_id received
10611     RPC_NET_ERROR           Error in socket call
10612     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10613     RPC_SHUTDOWN            Shutdown requested
10614     SS_ABORT                TCP connection broken
10615     SS_EXIT                 TCP connection closed
10616 
10617 \********************************************************************/
10618 {
10619    INT i, idx, routine_id, status;
10620    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10621    INT tid, flags;
10622    NET_COMMAND *nc_in, *nc_out;
10623    INT param_size, max_size;
10624    void *prpc_param[20];
10625    char str[1024], debug_line[1024], *return_buffer;
10626 
10627    /* return buffer must must use thread local storage multi-thread servers */
10628    if (!tls_size) {
10629       tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
10630       tls_buffer[tls_size].tid = ss_gettid();
10631       tls_buffer[tls_size].buffer = (char *) malloc(NET_BUFFER_SIZE);
10632       tls_size = 1;
10633    }
10634    for (i=0 ; i<tls_size ; i++)
10635       if (tls_buffer[i].tid == ss_gettid())
10636          break;
10637    if (i == tls_size){
10638       /* new thread -> allocate new buffer */
10639       tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size+1)*sizeof(TLS_POINTER));
10640       tls_buffer[tls_size].tid = ss_gettid();
10641       tls_buffer[tls_size].buffer = (char *) malloc(NET_BUFFER_SIZE);
10642       tls_size++;
10643    }
10644 
10645    return_buffer = tls_buffer[i].buffer;
10646    assert(return_buffer);
10647 
10648    /* extract pointer array to parameters */
10649    nc_in = (NET_COMMAND *) buffer;
10650    nc_out = (NET_COMMAND *) return_buffer;
10651 
10652    /* convert header format (byte swapping) */
10653    if (convert_flags) {
10654       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
10655       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
10656    }
10657 
10658    /* no result return in FAST TCP mode */
10659    if (nc_in->header.routine_id & TCP_FAST)
10660       sock = 0;
10661 
10662    /* find entry in rpc_list */
10663    routine_id = nc_in->header.routine_id & ~TCP_FAST;
10664 
10665    for (i = 0;; i++)
10666       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
10667          break;
10668    idx = i;
10669    if (rpc_list[i].id == 0) {
10670       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
10671       return RPC_INVALID_ID;
10672    }
10673 
10674    in_param_ptr = nc_in->param;
10675    out_param_ptr = nc_out->param;
10676 
10677    sprintf(debug_line, "%s(", rpc_list[idx].name);
10678 
10679    for (i = 0; rpc_list[idx].param[i].tid != 0; i++) {
10680       tid = rpc_list[idx].param[i].tid;
10681       flags = rpc_list[idx].param[i].flags;
10682 
10683       if (flags & RPC_IN) {
10684          param_size = ALIGN8(tid_size[tid]);
10685 
10686          if (tid == TID_STRING || tid == TID_LINK)
10687             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10688 
10689          if (flags & RPC_VARARRAY) {
10690             /* for arrays, the size is stored as a INT in front of the array */
10691             param_size = *((INT *) in_param_ptr);
10692             if (convert_flags)
10693                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
10694             param_size = ALIGN8(param_size);
10695 
10696             in_param_ptr += ALIGN8(sizeof(INT));
10697          }
10698 
10699          if (tid == TID_STRUCT)
10700             param_size = ALIGN8(rpc_list[idx].param[i].n);
10701 
10702          prpc_param[i] = in_param_ptr;
10703 
10704          /* convert data format */
10705          if (convert_flags) {
10706             if (flags & RPC_VARARRAY)
10707                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
10708             else
10709                rpc_convert_data(in_param_ptr, tid, flags,
10710                                 rpc_list[idx].param[i].n * tid_size[tid], convert_flags);
10711          }
10712 
10713          db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[idx].param[i].tid);
10714          if (rpc_list[idx].param[i].tid == TID_STRING) {
10715             /* check for long strings (db_create_record...) */
10716             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10717                strcat(debug_line, "\"");
10718                strcat(debug_line, str);
10719                strcat(debug_line, "\"");
10720             } else
10721                strcat(debug_line, "...");
10722          } else
10723             strcat(debug_line, str);
10724 
10725          in_param_ptr += param_size;
10726       }
10727 
10728       if (flags & RPC_OUT) {
10729          param_size = ALIGN8(tid_size[tid]);
10730 
10731          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10732             /* save maximum array length */
10733             max_size = *((INT *) in_param_ptr);
10734             if (convert_flags)
10735                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
10736             max_size = ALIGN8(max_size);
10737 
10738             *((INT *) out_param_ptr) = max_size;
10739 
10740             /* save space for return array length */
10741             out_param_ptr += ALIGN8(sizeof(INT));
10742 
10743             /* use maximum array length from input */
10744             param_size += max_size;
10745          }
10746 
10747          if (rpc_list[idx].param[i].tid == TID_STRUCT)
10748             param_size = ALIGN8(rpc_list[idx].param[i].n);
10749 
10750          if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > (INT) NET_BUFFER_SIZE) {
10751             cm_msg(MERROR, "rpc_execute",
10752                    "return parameters (%d) too large for network buffer (%d)",
10753                    (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, NET_BUFFER_SIZE);
10754             return RPC_EXCEED_BUFFER;
10755          }
10756 
10757          /* if parameter goes both directions, copy input to output */
10758          if (rpc_list[idx].param[i].flags & RPC_IN)
10759             memcpy(out_param_ptr, prpc_param[i], param_size);
10760 
10761          if (_debug_print && !(flags & RPC_IN))
10762             strcat(debug_line, "-");
10763 
10764          prpc_param[i] = out_param_ptr;
10765          out_param_ptr += param_size;
10766       }
10767 
10768       if (rpc_list[idx].param[i + 1].tid)
10769          strcat(debug_line, ", ");
10770    }
10771 
10772    strcat(debug_line, ")");
10773    rpc_debug_printf(debug_line);
10774 
10775    last_param_ptr = out_param_ptr;
10776 
10777   /*********************************\
10778   *   call dispatch function        *
10779   \*********************************/
10780    if (rpc_list[idx].dispatch)
10781       status = rpc_list[idx].dispatch(routine_id, prpc_param);
10782    else
10783       status = RPC_INVALID_ID;
10784 
10785    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
10786       status = RPC_SUCCESS;
10787 
10788    /* return immediately for closed down client connections */
10789    if (!sock && routine_id == RPC_ID_EXIT)
10790       return SS_EXIT;
10791 
10792    if (!sock && routine_id == RPC_ID_SHUTDOWN)
10793       return RPC_SHUTDOWN;
10794 
10795    /* Return if TCP connection broken */
10796    if (status == SS_ABORT)
10797       return SS_ABORT;
10798 
10799    /* if sock == 0, we are in FTCP mode and may not sent results */
10800    if (!sock)
10801       return RPC_SUCCESS;
10802 
10803    /* compress variable length arrays */
10804    out_param_ptr = nc_out->param;
10805    for (i = 0; rpc_list[idx].param[i].tid != 0; i++)
10806       if (rpc_list[idx].param[i].flags & RPC_OUT) {
10807          tid = rpc_list[idx].param[i].tid;
10808          flags = rpc_list[idx].param[i].flags;
10809          param_size = ALIGN8(tid_size[tid]);
10810 
10811          if (tid == TID_STRING) {
10812             max_size = *((INT *) out_param_ptr);
10813             param_size = strlen((char *) prpc_param[i]) + 1;
10814             param_size = ALIGN8(param_size);
10815 
10816             /* move string ALIGN8(sizeof(INT)) left */
10817             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
10818 
10819             /* move remaining parameters to end of string */
10820             memcpy(out_param_ptr + param_size,
10821                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
10822                    (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
10823          }
10824 
10825          if (flags & RPC_VARARRAY) {
10826             /* store array length at current out_param_ptr */
10827             max_size = *((INT *) out_param_ptr);
10828             param_size = *((INT *) prpc_param[i + 1]);
10829             *((INT *) out_param_ptr) = param_size; // store new array size
10830             if (convert_flags)
10831                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
10832 
10833             out_param_ptr += ALIGN8(sizeof(INT));  // step over array size
10834 
10835             param_size = ALIGN8(param_size);
10836 
10837             /* move remaining parameters to end of array */
10838             memcpy(out_param_ptr + param_size,
10839                    out_param_ptr + max_size,
10840                    (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
10841          }
10842 
10843          if (tid == TID_STRUCT)
10844             param_size = ALIGN8(rpc_list[idx].param[i].n);
10845 
10846          /* convert data format */
10847          if (convert_flags) {
10848             if (flags & RPC_VARARRAY)
10849                rpc_convert_data(out_param_ptr, tid,
10850                                 rpc_list[idx].param[i].flags | RPC_OUTGOING, param_size, convert_flags);
10851             else
10852                rpc_convert_data(out_param_ptr, tid,
10853                                 rpc_list[idx].param[i].flags | RPC_OUTGOING,
10854                                 rpc_list[idx].param[i].n * tid_size[tid], convert_flags);
10855          }
10856 
10857          out_param_ptr += param_size;
10858       }
10859 
10860    /* send return parameters */
10861    param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
10862    nc_out->header.routine_id = status;
10863    nc_out->header.param_size = param_size;
10864 
10865    /* convert header format (byte swapping) if necessary */
10866    if (convert_flags) {
10867       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
10868       rpc_convert_single(&nc_out->header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
10869    }
10870 
10871    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
10872 
10873    if (status < 0) {
10874       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
10875       return RPC_NET_ERROR;
10876    }
10877 
10878    /* print return buffer */
10879 /*
10880   printf("Return buffer, ID %d:\n", routine_id);
10881   for (i=0; i<param_size ; i++)
10882     {
10883     status = (char) nc_out->param[i];
10884     printf("%02X ", status);
10885     if (i%8 == 7)
10886       printf("\n");
10887     }
10888 */
10889    /* return SS_EXIT if RPC_EXIT is called */
10890    if (routine_id == RPC_ID_EXIT)
10891       return SS_EXIT;
10892 
10893    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
10894    if (routine_id == RPC_ID_SHUTDOWN)
10895       return RPC_SHUTDOWN;
10896 
10897    return RPC_SUCCESS;
10898 }
10899 
10900 
10901 /********************************************************************/
10902 INT rpc_execute_ascii(INT sock, char *buffer)
10903 /********************************************************************\
10904 
10905   Routine: rpc_execute_ascii
10906 
10907   Purpose: Execute a RPC command received over the network in ASCII
10908            mode
10909 
10910   Input:
10911     INT  sock               TCP socket to which the result should be
10912                             send back
10913 
10914     char *buffer            Command buffer
10915 
10916   Output:
10917     none
10918 
10919   Function value:
10920     RPC_SUCCESS             Successful completion
10921     RPC_INVALID_ID          Invalid routine_id received
10922     RPC_NET_ERROR           Error in socket call
10923     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10924     RPC_SHUTDOWN            Shutdown requested
10925     SS_ABORT                TCP connection broken
10926     SS_EXIT                 TCP connection closed
10927 
10928 \********************************************************************/
10929 {
10930 #define ASCII_BUFFER_SIZE 64500
10931 #define N_APARAM           1024
10932 
10933    INT i, j, idx, status, index_in;
10934    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10935    INT routine_id, tid, flags, array_tid, n_param;
10936    INT param_size, item_size, num_values;
10937    void *prpc_param[20];
10938    char *arpc_param[N_APARAM], *pc;
10939    char str[1024], debug_line[1024];
10940    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
10941    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
10942    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
10943 
10944    /* parse arguments */
10945    arpc_param[0] = buffer;
10946    for (i = 1; i < N_APARAM; i++) {
10947       arpc_param[i] = strchr(arpc_param[i - 1], '&');
10948       if (arpc_param[i] == NULL)
10949          break;
10950       *arpc_param[i] = 0;
10951       arpc_param[i]++;
10952    }
10953 
10954    /* decode '%' */
10955    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
10956       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
10957          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
10958             str[0] = pc[1];
10959             str[1] = pc[2];
10960             str[2] = 0;
10961             sscanf(str, "%02X", &i);
10962 
10963             *pc++ = i;
10964             while (pc[2]) {
10965                pc[0] = pc[2];
10966                pc++;
10967             }
10968          }
10969       }
10970 
10971    /* find entry in rpc_list */
10972    for (i = 0;; i++)
10973       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
10974          break;
10975    idx = i;
10976    routine_id = rpc_list[i].id;
10977    if (rpc_list[i].id == 0) {
10978       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
10979       return RPC_INVALID_ID;
10980    }
10981 
10982    in_param_ptr = buffer1;
10983    out_param_ptr = buffer2;
10984    index_in = 1;
10985 
10986    sprintf(debug_line, "%s(", rpc_list[idx].name);
10987 
10988    for (i = 0; rpc_list[idx].param[i].tid != 0; i++) {
10989       tid = rpc_list[idx].param[i].tid;
10990       flags = rpc_list[idx].param[i].flags;
10991 
10992       if (flags & RPC_IN) {
10993          if (flags & RPC_VARARRAY) {
10994             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
10995 
10996             prpc_param[i] = in_param_ptr;
10997             for (j = 0; j < n_param; j++) {
10998                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
10999                in_param_ptr += param_size;
11000             }
11001             in_param_ptr = (char *) ALIGN8(((POINTER_T) in_param_ptr));
11002 
11003             strcat(debug_line, "<array>");
11004          } else {
11005             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
11006             param_size = ALIGN8(param_size);
11007 
11008             if (tid == TID_STRING || tid == TID_LINK)
11009                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11010 
11011             /*
11012                if (tid == TID_STRUCT)
11013                param_size = ALIGN8( rpc_list[idx].param[i].n );
11014              */
11015             prpc_param[i] = in_param_ptr;
11016 
11017             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[idx].param[i].tid);
11018             if (rpc_list[idx].param[i].tid == TID_STRING) {
11019                /* check for long strings (db_create_record...) */
11020                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11021                   strcat(debug_line, "\"");
11022                   strcat(debug_line, str);
11023                   strcat(debug_line, "\"");
11024                } else
11025                   strcat(debug_line, "...");
11026             } else
11027                strcat(debug_line, str);
11028 
11029             in_param_ptr += param_size;
11030          }
11031 
11032          if ((POINTER_T) in_param_ptr - (POINTER_T) buffer1 > ASCII_BUFFER_SIZE) {
11033             cm_msg(MERROR, "rpc_ascii_execute",
11034                    "parameters (%d) too large for network buffer (%d)", param_size, ASCII_BUFFER_SIZE);
11035             return RPC_EXCEED_BUFFER;
11036          }
11037 
11038       }
11039 
11040       if (flags & RPC_OUT) {
11041          param_size = ALIGN8(tid_size[tid]);
11042 
11043          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11044             /* reserve maximum array length */
11045             param_size = atoi(arpc_param[index_in]);
11046             param_size = ALIGN8(param_size);
11047          }
11048 
11049 /*
11050       if (rpc_list[idx].param[i].tid == TID_STRUCT)
11051         param_size = ALIGN8( rpc_list[idx].param[i].n );
11052 */
11053          if ((POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size > ASCII_BUFFER_SIZE) {
11054             cm_msg(MERROR, "rpc_execute",
11055                    "return parameters (%d) too large for network buffer (%d)",
11056                    (POINTER_T) out_param_ptr - (POINTER_T) buffer2 + param_size, ASCII_BUFFER_SIZE);
11057             return RPC_EXCEED_BUFFER;
11058          }
11059 
11060          /* if parameter goes both directions, copy input to output */
11061          if (rpc_list[idx].param[i].flags & RPC_IN)
11062             memcpy(out_param_ptr, prpc_param[i], param_size);
11063 
11064          if (!(flags & RPC_IN))
11065             strcat(debug_line, "-");
11066 
11067          prpc_param[i] = out_param_ptr;
11068          out_param_ptr += param_size;
11069       }
11070 
11071       if (rpc_list[idx].param[i + 1].tid)
11072          strcat(debug_line, ", ");
11073    }
11074 
11075    strcat(debug_line, ")");
11076    rpc_debug_printf(debug_line);
11077 
11078    last_param_ptr = out_param_ptr;
11079 
11080    /*********************************\
11081    *   call dispatch function        *
11082    \*********************************/
11083 
11084    if (rpc_list[idx].dispatch)
11085       status = rpc_list[idx].dispatch(routine_id, prpc_param);
11086    else
11087       status = RPC_INVALID_ID;
11088 
11089    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
11090       status = RPC_SUCCESS;
11091 
11092    /* Return if TCP connection broken */
11093    if (status == SS_ABORT)
11094       return SS_ABORT;
11095 
11096    /* if sock == 0, we are in FTCP mode and may not sent results */
11097    if (!sock)
11098       return RPC_SUCCESS;
11099 
11100    /* send return status */
11101    out_param_ptr = return_buffer;
11102    sprintf(out_param_ptr, "%d", status);
11103    out_param_ptr += strlen(out_param_ptr);
11104 
11105    /* convert return parameters */
11106    for (i = 0; rpc_list[idx].param[i].tid != 0; i++)
11107       if (rpc_list[idx].param[i].flags & RPC_OUT) {
11108          *out_param_ptr++ = '&';
11109 
11110          tid = rpc_list[idx].param[i].tid;
11111          flags = rpc_list[idx].param[i].flags;
11112          param_size = ALIGN8(tid_size[tid]);
11113 
11114          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
11115             strcpy(out_param_ptr, (char *) prpc_param[i]);
11116             param_size = strlen((char *) prpc_param[i]);
11117          }
11118 
11119          else if (flags & RPC_VARARRAY) {
11120             if (rpc_list[idx].id == RPC_BM_RECEIVE_EVENT) {
11121                param_size = *((INT *) prpc_param[i + 1]);
11122                /* write number of bytes to output */
11123                sprintf(out_param_ptr, "%d", param_size);
11124                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
11125                memcpy(out_param_ptr, prpc_param[i], param_size);
11126                out_param_ptr += param_size;
11127                *out_param_ptr = 0;
11128             } else {
11129                if (rpc_list[idx].id == RPC_DB_GET_DATA1) {
11130                   param_size = *((INT *) prpc_param[i + 1]);
11131                   array_tid = *((INT *) prpc_param[i + 2]);
11132                   num_values = *((INT *) prpc_param[i + 3]);
11133                } else if (rpc_list[idx].id == RPC_DB_GET_DATA_INDEX) {
11134                   param_size = *((INT *) prpc_param[i + 1]);
11135                   array_tid = *((INT *) prpc_param[i + 3]);
11136                   num_values = 1;
11137                } else if (rpc_list[idx].id == RPC_HS_READ) {
11138                   param_size = *((INT *) prpc_param[i + 1]);
11139                   if (i == 6) {
11140                      array_tid = TID_DWORD;
11141                      num_values = param_size / sizeof(DWORD);
11142                   } else {
11143                      array_tid = *((INT *) prpc_param[10]);
11144                      num_values = *((INT *) prpc_param[11]);
11145                   }
11146                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
11147 
11148                   param_size = *((INT *) prpc_param[i + 1]);
11149                   array_tid = tid;
11150                   if (tid == TID_STRING)
11151                      num_values = param_size / NAME_LENGTH;
11152                   else
11153                      num_values = param_size / tid_size[tid];
11154                }
11155 
11156                /* derive size of individual item */
11157                if (array_tid == TID_STRING)
11158                   item_size = param_size / num_values;
11159                else
11160                   item_size = tid_size[array_tid];
11161 
11162                /* write number of elements to output */
11163                sprintf(out_param_ptr, "%d", num_values);
11164                out_param_ptr += strlen(out_param_ptr);
11165 
11166                /* write array of values to output */
11167                for (j = 0; j < num_values; j++) {
11168                   *out_param_ptr++ = '&';
11169                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
11170                   out_param_ptr += strlen(out_param_ptr);
11171                }
11172             }
11173          }
11174 
11175 /*
11176       else if (tid == TID_STRUCT)
11177         param_size = ALIGN8( rpc_list[idx].param[i].n );
11178 */
11179          else
11180             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
11181 
11182          out_param_ptr += strlen(out_param_ptr);
11183 
11184          if ((POINTER_T) out_param_ptr - (POINTER_T) return_buffer > ASCII_BUFFER_SIZE) {
11185             cm_msg(MERROR, "rpc_execute",
11186                    "return parameter (%d) too large for network buffer (%d)", param_size, ASCII_BUFFER_SIZE);
11187             return RPC_EXCEED_BUFFER;
11188          }
11189       }
11190 
11191    /* send return parameters */
11192    param_size = (POINTER_T) out_param_ptr - (POINTER_T) return_buffer + 1;
11193 
11194    status = send_tcp(sock, return_buffer, param_size, 0);
11195 
11196    if (status < 0) {
11197       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11198       return RPC_NET_ERROR;
11199    }
11200 
11201    /* print return buffer */
11202    if (strlen(return_buffer) > sizeof(debug_line)) {
11203       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
11204       strcat(debug_line, "...");
11205    } else
11206       sprintf(debug_line, "-> %s", return_buffer);
11207    rpc_debug_printf(debug_line);
11208 
11209    /* return SS_EXIT if RPC_EXIT is called */
11210    if (routine_id == RPC_ID_EXIT)
11211       return SS_EXIT;
11212 
11213    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11214    if (routine_id == RPC_ID_SHUTDOWN)
11215       return RPC_SHUTDOWN;
11216 
11217    return RPC_SUCCESS;
11218 }
11219 
11220 
11221 /********************************************************************/
11222 INT rpc_server_accept(int lsock)
11223 /********************************************************************\
11224 
11225   Routine: rpc_server_accept
11226 
11227   Purpose: Accept new incoming connections
11228 
11229   Input:
11230     INT    lscok            Listen socket
11231 
11232   Output:
11233     none
11234 
11235   Function value:
11236     RPC_SUCCESS             Successful completion
11237     RPC_NET_ERROR           Error in socket call
11238     RPC_CONNCLOSED          Connection was closed
11239     RPC_SHUTDOWN            Listener shutdown
11240     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11241 
11242 \********************************************************************/
11243 {
11244    INT idx, i;
11245    unsigned int size;
11246    int status;
11247    char command;
11248    INT sock;
11249    char version[NAME_LENGTH], v1[32];
11250    char experiment[NAME_LENGTH];
11251    INT port1, port2, port3;
11252    char *ptr;
11253    struct sockaddr_in acc_addr;
11254    struct hostent *phe;
11255    char str[100];
11256    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11257    char debug_str[30];
11258    char *argv[10];
11259    char net_buffer[256];
11260    struct linger ling;
11261 
11262    static struct callback_addr callback;
11263 
11264    if (lsock > 0) {
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    } else {
11271       /* lsock is stdin -> already connected from inetd */
11272 
11273       size = sizeof(acc_addr);
11274       sock = lsock;
11275       getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11276    }
11277 
11278    /* receive string with timeout */
11279    i = recv_string(sock, net_buffer, 256, 10000);
11280    rpc_debug_printf("Received command: %s", net_buffer);
11281 
11282    if (i > 0) {
11283       command = (char) toupper(net_buffer[0]);
11284 
11285       switch (command) {
11286       case 'S':
11287 
11288          /*----------- shutdown listener ----------------------*/
11289          closesocket(sock);
11290          return RPC_SHUTDOWN;
11291 
11292       case 'I':
11293 
11294          /*----------- return available experiments -----------*/
11295          cm_scan_experiments();
11296          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11297             rpc_debug_printf("Return experiment: %s", exptab[i].name);
11298             sprintf(str, "%s", exptab[i].name);
11299             send(sock, str, strlen(str) + 1, 0);
11300          }
11301          send(sock, "", 1, 0);
11302          closesocket(sock);
11303          break;
11304 
11305       case 'C':
11306 
11307          /*----------- connect to experiment -----------*/
11308 
11309          /* get callback information */
11310          callback.experiment[0] = 0;
11311          port1 = port2 = version[0] = 0;
11312 
11313          //printf("net buffer \'%s\'\n", net_buffer);
11314 
11315          /* parse string in format "C port1 port2 port3 version expt" */
11316          /* example: C 51046 45838 56832 2.0.0 alpha */
11317          
11318          port1 = strtoul(net_buffer + 2, &ptr, 0);
11319          port2 = strtoul(ptr, &ptr, 0);
11320          port3 = strtoul(ptr, &ptr, 0);
11321 
11322          while (*ptr == ' ')
11323            ptr++;
11324 
11325          i = 0;
11326          for (; *ptr!=0 && *ptr!=' ' && i<(int)sizeof(version)-1; )
11327            version[i++] = *ptr++;
11328 
11329          // ensure that we do not overwrite buffer "version"
11330          assert(i<(int)sizeof(version));
11331          version[i] = 0;
11332 
11333          // skip wjatever is left from the "version" string
11334          for (; *ptr!=0 && *ptr!=' '; )
11335            ptr++;
11336 
11337          while (*ptr == ' ')
11338            ptr++;
11339 
11340          i = 0;
11341          for (; *ptr!=0 && *ptr!=' ' && *ptr!='\n' && *ptr!='\r' && i<(int)sizeof(experiment)-1; )
11342            experiment[i++] = *ptr++;
11343 
11344          // ensure that we do not overwrite buffer "experiment"
11345          assert(i<(int)sizeof(experiment));
11346          experiment[i] = 0;
11347 
11348          strlcpy(callback.experiment, experiment, NAME_LENGTH);
11349 
11350          /* print warning if version patch level doesn't agree */
11351          strlcpy(v1, version, sizeof(v1));
11352          if (strchr(v1, '.'))
11353             if (strchr(strchr(v1, '.') + 1, '.'))
11354                *strchr(strchr(v1, '.') + 1, '.') = 0;
11355 
11356          strlcpy(str, cm_get_version(), sizeof(str));
11357          if (strchr(str, '.'))
11358             if (strchr(strchr(str, '.') + 1, '.'))
11359                *strchr(strchr(str, '.') + 1, '.') = 0;
11360 
11361          if (strcmp(v1, str) != 0) {
11362             sprintf(str, "client MIDAS version %s differs from local version %s", version, cm_get_version());
11363             cm_msg(MERROR, "rpc_server_accept", str);
11364 
11365             sprintf(str, "received string: %s", net_buffer + 2);
11366             cm_msg(MERROR, "rpc_server_accept", str);
11367          }
11368 
11369          callback.host_port1 = (short) port1;
11370          callback.host_port2 = (short) port2;
11371          callback.host_port3 = (short) port3;
11372          callback.debug = _debug_mode;
11373 
11374          /* get the name of the remote host */
11375 #ifdef OS_VXWORKS
11376          {
11377             INT status;
11378             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11379             if (status != 0) {
11380                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11381                break;
11382             }
11383          }
11384 #else
11385          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11386          if (phe == NULL) {
11387             /* use IP number instead */
11388             strlcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr), HOST_NAME_LENGTH);
11389          } else
11390             strlcpy(callback.host_name, phe->h_name, HOST_NAME_LENGTH);
11391 #endif
11392 
11393          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11394             /* update experiment definition */
11395             cm_scan_experiments();
11396 
11397             /* lookup experiment */
11398             if (equal_ustring(callback.experiment, "Default"))
11399                idx = 0;
11400             else
11401                for (idx = 0; idx < MAX_EXPERIMENT && exptab[idx].name[0]; idx++)
11402                   if (equal_ustring(callback.experiment, exptab[idx].name))
11403                      break;
11404 
11405             if (idx == MAX_EXPERIMENT || exptab[idx].name[0] == 0) {
11406                sprintf(str, "experiment \'%s\' not defined in exptab\r", callback.experiment);
11407                cm_msg(MERROR, "rpc_server_accept", str);
11408 
11409                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11410                closesocket(sock);
11411                break;
11412             }
11413 
11414             strlcpy(callback.directory, exptab[idx].directory, MAX_STRING_LENGTH);
11415             strlcpy(callback.user, exptab[idx].user, NAME_LENGTH);
11416 
11417             /* create a new process */
11418             sprintf(host_port1_str, "%d", callback.host_port1);
11419             sprintf(host_port2_str, "%d", callback.host_port2);
11420             sprintf(host_port3_str, "%d", callback.host_port3);
11421             sprintf(debug_str, "%d", callback.debug);
11422 
11423             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11424             argv[1] = callback.host_name;
11425             argv[2] = host_port1_str;
11426             argv[3] = host_port2_str;
11427             argv[4] = host_port3_str;
11428             argv[5] = debug_str;
11429             argv[6] = callback.experiment;
11430             argv[7] = callback.directory;
11431             argv[8] = callback.user;
11432             argv[9] = NULL;
11433 
11434             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
11435                              argv[0], argv[1], argv[2], argv[3], argv[4],
11436                              argv[5], argv[6], argv[7], argv[8], argv[9]);
11437 
11438             status = ss_spawnv(P_NOWAIT, (char *) rpc_get_server_option(RPC_OSERVER_NAME), argv);
11439 
11440             if (status != SS_SUCCESS) {
11441                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
11442 
11443                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
11444                send(sock, str, strlen(str) + 1, 0);
11445                closesocket(sock);
11446                break;
11447             }
11448 
11449             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11450             send(sock, str, strlen(str) + 1, 0);
11451             closesocket(sock);
11452          } else {
11453             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11454             send(sock, str, strlen(str) + 1, 0);
11455             closesocket(sock);
11456          }
11457 
11458          /* look for next free entry */
11459          for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
11460             if (_server_acception[idx].recv_sock == 0)
11461                break;
11462          if (idx == MAX_RPC_CONNECTION)
11463             return RPC_NET_ERROR;
11464          callback.index = idx;
11465 
11466         /*----- multi thread server ------------------------*/
11467          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11468             ss_thread_create(rpc_server_thread, (void *) (&callback));
11469 
11470         /*----- single thread server -----------------------*/
11471          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
11472              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11473             rpc_server_callback(&callback);
11474 
11475          break;
11476 
11477       default:
11478          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
11479          closesocket(sock);
11480          break;
11481 
11482       }
11483    } else {                     /* if i>0 */
11484 
11485       /* lingering needed for PCTCP */
11486       ling.l_onoff = 1;
11487       ling.l_linger = 0;
11488       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11489       closesocket(sock);
11490    }
11491 
11492    return RPC_SUCCESS;
11493 }
11494 
11495 /********************************************************************/
11496 INT rpc_client_accept(int lsock)
11497 /********************************************************************\
11498 
11499   Routine: rpc_client_accept
11500 
11501   Purpose: Accept new incoming connections as a client
11502 
11503   Input:
11504     INT    lsock            Listen socket
11505 
11506   Output:
11507     none
11508 
11509   Function value:
11510     RPC_SUCCESS             Successful completion
11511     RPC_NET_ERROR           Error in socket call
11512     RPC_CONNCLOSED          Connection was closed
11513     RPC_SHUTDOWN            Listener shutdown
11514     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11515 
11516 \********************************************************************/
11517 {
11518    INT idx, i, version, status;
11519    unsigned int size;
11520    int sock;
11521    struct sockaddr_in acc_addr;
11522    INT client_hw_type = 0, hw_type;
11523    char str[100], client_program[NAME_LENGTH];
11524    char host_name[HOST_NAME_LENGTH];
11525    INT convert_flags;
11526    char net_buffer[256], *p;
11527 
11528    size = sizeof(acc_addr);
11529    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11530 
11531    if (sock == -1)
11532       return RPC_NET_ERROR;
11533 
11534    /* get the name of the calling host */
11535 /* outcommented for speed reasons SR 7.10.98
11536 #ifdef OS_VXWORKS
11537   {
11538   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
11539   if (status != 0)
11540     strcpy(host_name, "unknown");
11541   }
11542 #else
11543   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11544   if (phe == NULL)
11545     strcpy(host_name, "unknown");
11546   strcpy(host_name, phe->h_name);
11547 #endif
11548 */
11549    strcpy(host_name, "(unknown)");
11550 
11551    strcpy(client_program, "(unknown)");
11552 
11553    /* look for next free entry */
11554    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++)
11555       if (_server_acception[idx].recv_sock == 0)
11556          break;
11557    if (idx == MAX_RPC_CONNECTION) {
11558       closesocket(sock);
11559       return RPC_NET_ERROR;
11560    }
11561 
11562    /* receive string with timeout */
11563    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
11564    if (i <= 0) {
11565       closesocket(sock);
11566       return RPC_NET_ERROR;
11567    }
11568 
11569    /* get remote computer info */
11570    p = strtok(net_buffer, " ");
11571    if (p != NULL) {
11572       client_hw_type = atoi(p);
11573       p = strtok(NULL, " ");
11574    }
11575    if (p != NULL) {
11576       version = atoi(p);
11577       p = strtok(NULL, " ");
11578    }
11579    if (p != NULL) {
11580       strlcpy(client_program, p, sizeof(client_program));
11581       p = strtok(NULL, " ");
11582    }
11583    if (p != NULL) {
11584       strlcpy(host_name, p, sizeof(host_name));
11585       p = strtok(NULL, " ");
11586    }
11587 
11588    if (0)
11589       printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
11590 
11591    /* save information in _server_acception structure */
11592    _server_acception[idx].recv_sock = sock;
11593    _server_acception[idx].send_sock = 0;
11594    _server_acception[idx].event_sock = 0;
11595    _server_acception[idx].remote_hw_type = client_hw_type;
11596    strcpy(_server_acception[idx].host_name, host_name);
11597    strcpy(_server_acception[idx].prog_name, client_program);
11598    _server_acception[idx].tid = ss_gettid();
11599    _server_acception[idx].last_activity = ss_millitime();
11600    _server_acception[idx].watchdog_timeout = 0;
11601 
11602    /* send my own computer id */
11603    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11604    sprintf(str, "%d %s", hw_type, cm_get_version());
11605    status = send(sock, str, strlen(str) + 1, 0);
11606    if (status != (INT) strlen(str) + 1)
11607       return RPC_NET_ERROR;
11608 
11609    rpc_set_server_acception(idx + 1);
11610    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11611    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11612 
11613    /* set callback function for ss_suspend */
11614    ss_suspend_set_dispatch(CH_SERVER, _server_acception, (int (*)(void)) rpc_server_receive);
11615 
11616    return RPC_SUCCESS;
11617 }
11618 
11619 /********************************************************************/
11620 INT rpc_server_callback(struct callback_addr * pcallback)
11621 /********************************************************************\
11622 
11623   Routine: rpc_server_callback
11624 
11625   Purpose: Callback a remote client. Setup _server_acception entry
11626            with optional conversion flags and establish two-way
11627            TCP connection.
11628 
11629   Input:
11630     callback_addr pcallback Pointer to a callback structure
11631 
11632   Output:
11633     none
11634 
11635   Function value:
11636     RPC_SUCCESS             Successful completion
11637 
11638 \********************************************************************/
11639 {
11640    INT idx, status;
11641    int recv_sock, send_sock, event_sock;
11642    struct sockaddr_in bind_addr;
11643    struct hostent *phe;
11644    char str[100], client_program[NAME_LENGTH];
11645    char host_name[HOST_NAME_LENGTH];
11646    INT client_hw_type, hw_type;
11647    INT convert_flags;
11648    char net_buffer[256];
11649    struct callback_addr callback;
11650    char *p;
11651    int flag;
11652 
11653    /* copy callback information */
11654    memcpy(&callback, pcallback, sizeof(callback));
11655    idx = callback.index;
11656 
11657    /* create new sockets for TCP */
11658    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
11659    send_sock = socket(AF_INET, SOCK_STREAM, 0);
11660    event_sock = socket(AF_INET, SOCK_STREAM, 0);
11661    if (event_sock == -1)
11662       return RPC_NET_ERROR;
11663 
11664    /* callback to remote node */
11665    memset(&bind_addr, 0, sizeof(bind_addr));
11666    bind_addr.sin_family = AF_INET;
11667    bind_addr.sin_port = htons(callback.host_port1);
11668 
11669 #ifdef OS_VXWORKS
11670    {
11671       INT host_addr;
11672 
11673       host_addr = hostGetByName(callback.host_name);
11674       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
11675    }
11676 #else
11677    phe = gethostbyname(callback.host_name);
11678    if (phe == NULL) {
11679       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
11680       return RPC_NET_ERROR;
11681    }
11682    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
11683 #endif
11684 
11685    /* connect receive socket */
11686 #ifdef OS_UNIX
11687    do {
11688       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
11689 
11690       /* don't return if an alarm signal was cought */
11691    } while (status == -1 && errno == EINTR);
11692 #else
11693    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11694 #endif
11695 
11696    if (status != 0) {
11697       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
11698       goto error;
11699    }
11700 
11701    bind_addr.sin_port = htons(callback.host_port2);
11702 
11703    /* connect send socket */
11704 #ifdef OS_UNIX
11705    do {
11706       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11707 
11708       /* don't return if an alarm signal was cought */
11709    } while (status == -1 && errno == EINTR);
11710 #else
11711    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11712 #endif
11713 
11714    if (status != 0) {
11715       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
11716       goto error;
11717    }
11718 
11719    bind_addr.sin_port = htons(callback.host_port3);
11720 
11721    /* connect event socket */
11722 #ifdef OS_UNIX
11723    do {
11724       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11725 
11726       /* don't return if an alarm signal was cought */
11727    } while (status == -1 && errno == EINTR);
11728 #else
11729    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11730 #endif
11731 
11732    if (status != 0) {
11733       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
11734       goto error;
11735    }
11736 
11737    /* increase receive buffer size to 64k */
11738 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
11739    flag = 0x10000;
11740    setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
11741 #endif
11742 
11743    if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
11744       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
11745       goto error;
11746    }
11747 
11748    //printf("rpc_server_callback: \'%s\'\n", net_buffer);
11749 
11750    /* get remote computer info */
11751    client_hw_type = strtoul(net_buffer, &p, 0);
11752 
11753    while (*p == ' ')
11754      p++;
11755 
11756    strlcpy(client_program, p, sizeof(client_program));
11757 
11758    //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
11759 
11760    /* get the name of the remote host */
11761 #ifdef OS_VXWORKS
11762    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
11763    if (status != 0)
11764       strcpy(host_name, "unknown");
11765 #else
11766    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
11767    if (phe == NULL)
11768       strcpy(host_name, "unknown");
11769    else
11770       strcpy(host_name, phe->h_name);
11771 #endif
11772 
11773    /* save information in _server_acception structure */
11774    _server_acception[idx].recv_sock = recv_sock;
11775    _server_acception[idx].send_sock = send_sock;
11776    _server_acception[idx].event_sock = event_sock;
11777    _server_acception[idx].remote_hw_type = client_hw_type;
11778    strcpy(_server_acception[idx].host_name, host_name);
11779    strcpy(_server_acception[idx].prog_name, client_program);
11780    _server_acception[idx].tid = ss_gettid();
11781    _server_acception[idx].last_activity = ss_millitime();
11782    _server_acception[idx].watchdog_timeout = 0;
11783 
11784    /* send my own computer id */
11785    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11786    sprintf(str, "%d", hw_type);
11787    send(recv_sock, str, strlen(str) + 1, 0);
11788 
11789    rpc_set_server_acception(idx + 1);
11790    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11791    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11792 
11793    /* set callback function for ss_suspend */
11794    ss_suspend_set_dispatch(CH_SERVER, _server_acception, (int (*)(void)) rpc_server_receive);
11795 
11796    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11797       rpc_debug_printf("Connection to %s:%s established\n",
11798                        _server_acception[idx].host_name, _server_acception[idx].prog_name);
11799 
11800    return RPC_SUCCESS;
11801 
11802  error:
11803 
11804    closesocket(recv_sock);
11805    closesocket(send_sock);
11806    closesocket(event_sock);
11807 
11808    return RPC_NET_ERROR;
11809 }
11810 
11811 
11812 /********************************************************************/
11813 INT rpc_server_thread(void *pointer)
11814 /********************************************************************\
11815 
11816   Routine: rpc_server_thread
11817 
11818   Purpose: New thread for a multi-threaded server. Callback to the
11819            client and process RPC requests.
11820 
11821   Input:
11822     vcoid  pointer          pointer to callback_addr structure.
11823 
11824   Output:
11825     none
11826 
11827   Function value:
11828     RPC_SUCCESS             Successful completion
11829 
11830 \********************************************************************/
11831 {
11832    struct callback_addr callback;
11833    int status, mutex_alarm, mutex_elog, mutex_history, mutex_msg;
11834    static DWORD last_checked = 0;
11835 
11836    memcpy(&callback, pointer, sizeof(callback));
11837 
11838    status = rpc_server_callback(&callback);
11839 
11840    if (status != RPC_SUCCESS)
11841       return status;
11842 
11843    /* create alarm and elog mutexes */
11844    ss_mutex_create("ALARM", &mutex_alarm);
11845    ss_mutex_create("ELOG", &mutex_elog);
11846    ss_mutex_create("HISTORY", &mutex_history);
11847    ss_mutex_create("MSG", &mutex_msg);
11848    cm_set_experiment_mutex(mutex_alarm, mutex_elog, mutex_history, mutex_msg);
11849 
11850    do {
11851       status = ss_suspend(5000, 0);
11852 
11853       if (rpc_check_channels() == RPC_NET_ERROR)
11854          break;
11855 
11856       /* check alarms every 10 seconds */
11857       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
11858          al_check();
11859          last_checked = ss_time();
11860       }
11861 
11862    } while (status != SS_ABORT && status != SS_EXIT);
11863 
11864    /* delete entry in suspend table for this thread */
11865    ss_suspend_exit();
11866 
11867    return RPC_SUCCESS;
11868 }
11869 
11870 
11871 /********************************************************************/
11872 INT rpc_server_receive(INT idx, int sock, BOOL check)
11873 /********************************************************************\
11874 
11875   Routine: rpc_server_receive
11876 
11877   Purpose: Receive rpc commands and execute them. Close the connection
11878            if client has broken TCP pipe.
11879 
11880   Input:
11881     INT    idx              Index to _server_acception structure in-
11882                             dicating which connection got data.
11883     int    sock             Socket which got data
11884     BOOL   check            If TRUE, only check if connection is
11885                             broken. This may be called via
11886                             bm_receive_event/ss_suspend(..,MSG_BM)
11887 
11888   Output:
11889     none
11890 
11891   Function value:
11892     RPC_SUCCESS             Successful completion
11893     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
11894     SS_EXIT                 Server connection was closed
11895     SS_ABORT                Server connection was broken
11896 
11897 \********************************************************************/
11898 {
11899    INT status, n_received;
11900    INT remaining, *pbh, start_time;
11901    char test_buffer[256], str[80];
11902    EVENT_HEADER *pevent;
11903 
11904    /* init network buffer */
11905    if (_net_recv_buffer_size == 0) {
11906       _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
11907       if (_net_recv_buffer == NULL) {
11908          cm_msg(MERROR, "rpc_server_receive", "not enough memory to allocate network buffer");
11909          return RPC_EXCEED_BUFFER;
11910       }
11911       _net_recv_buffer_size = NET_BUFFER_SIZE;
11912    }
11913 
11914    /* only check if TCP connection is broken */
11915    if (check) {
11916       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
11917 
11918       if (n_received == -1)
11919          cm_msg(MERROR, "rpc_server_receive",
11920                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
11921                 n_received, errno, strerror(errno));
11922 
11923       if (n_received <= 0)
11924          return SS_ABORT;
11925 
11926       return SS_SUCCESS;
11927    }
11928 
11929    remaining = 0;
11930 
11931    /* receive command */
11932    if (sock == _server_acception[idx].recv_sock) {
11933       do {
11934          if (_server_acception[idx].remote_hw_type == DR_ASCII)
11935             n_received = recv_string(_server_acception[idx].recv_sock, _net_recv_buffer,
11936                                      NET_BUFFER_SIZE, 10000);
11937          else
11938             n_received = recv_tcp_server(idx, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
11939 
11940          if (n_received <= 0) {
11941             status = SS_ABORT;
11942             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort", n_received);
11943             goto error;
11944          }
11945 
11946          rpc_set_server_acception(idx + 1);
11947 
11948          if (_server_acception[idx].remote_hw_type == DR_ASCII)
11949             status = rpc_execute_ascii(_server_acception[idx].recv_sock, _net_recv_buffer);
11950          else
11951             status = rpc_execute(_server_acception[idx].recv_sock,
11952                                  _net_recv_buffer, _server_acception[idx].convert_flags);
11953 
11954          if (status == SS_ABORT) {
11955             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort", status);
11956             goto error;
11957          }
11958 
11959          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
11960             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11961                rpc_debug_printf("Connection to %s:%s closed\n",
11962                                 _server_acception[idx].host_name, _server_acception[idx].prog_name);
11963             goto exit;
11964          }
11965 
11966       } while (remaining);
11967    } else {
11968       /* receive event */
11969       if (sock == _server_acception[idx].event_sock) {
11970          start_time = ss_millitime();
11971 
11972          do {
11973             n_received = recv_event_server(idx, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
11974 
11975             if (n_received <= 0) {
11976                status = SS_ABORT;
11977                cm_msg(MERROR, "rpc_server_receive", "recv_event_server() returned %d, abort", n_received);
11978                goto error;
11979             }
11980 
11981             /* send event to buffer */
11982             pbh = (INT *) _net_recv_buffer;
11983             pevent = (EVENT_HEADER *) (pbh + 1);
11984 
11985             status = bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
11986             if (status != BM_SUCCESS)
11987                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d", status);
11988 
11989             /* repeat for maximum 0.5 sec */
11990          } while (ss_millitime() - start_time < 500 && remaining);
11991       }
11992    }
11993 
11994    return RPC_SUCCESS;
11995 
11996  error:
11997 
11998    strlcpy(str, _server_acception[idx].host_name, sizeof(str));
11999    if (strchr(str, '.'))
12000       *strchr(str, '.') = 0;
12001    cm_msg(MTALK, "rpc_server_receive", "Program \'%s\' on host \'%s\' aborted",
12002           _server_acception[idx].prog_name, str);
12003 
12004  exit:
12005 
12006    /* disconnect from experiment as MIDAS server */
12007    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
12008       HNDLE hDB, hKey;
12009 
12010       cm_get_experiment_database(&hDB, &hKey);
12011 
12012       /* only disconnect from experiment if previously connected.
12013          Necessary for pure RPC servers (RPC_SRVR) */
12014       if (hDB) {
12015 #ifdef LOCAL_ROUTINES
12016          ss_alarm(0, cm_watchdog);
12017 #endif
12018 
12019          cm_delete_client_info(hDB, 0);
12020 
12021          bm_close_all_buffers();
12022          db_close_all_databases();
12023 
12024          rpc_deregister_functions();
12025 
12026          cm_set_experiment_database(0, 0);
12027 
12028          _msg_buffer = 0;
12029       }
12030    }
12031 
12032    /* close server connection */
12033    if (_server_acception[idx].recv_sock)
12034       closesocket(_server_acception[idx].recv_sock);
12035    if (_server_acception[idx].send_sock)
12036       closesocket(_server_acception[idx].send_sock);
12037    if (_server_acception[idx].event_sock)
12038       closesocket(_server_acception[idx].event_sock);
12039 
12040    /* free TCP cache */
12041    M_FREE(_server_acception[idx].net_buffer);
12042    _server_acception[idx].net_buffer = NULL;
12043 
12044    /* mark this entry as invalid */
12045    memset(&_server_acception[idx], 0, sizeof(RPC_SERVER_ACCEPTION));
12046 
12047    /* signal caller a shutdonw */
12048    if (status == RPC_SHUTDOWN)
12049       return status;
12050 
12051    /* don't abort if other than main connection is broken */
12052    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
12053       return SS_SUCCESS;
12054 
12055    return status;
12056 }
12057 
12058 
12059 /********************************************************************/
12060 INT rpc_server_shutdown(void)
12061 /********************************************************************\
12062 
12063   Routine: rpc_server_shutdown
12064 
12065   Purpose: Shutdown RPC server, abort all connections
12066 
12067   Input:
12068     none
12069 
12070   Output:
12071     none
12072 
12073   Function value:
12074     RPC_SUCCESS             Successful completion
12075 
12076 \********************************************************************/
12077 {
12078    INT i;
12079    struct linger ling;
12080 
12081    /* close all open connections */
12082    for (i = 0; i < MAX_RPC_CONNECTION; i++)
12083       if (_server_acception[i].recv_sock != 0) {
12084          /* lingering needed for PCTCP */
12085          ling.l_onoff = 1;
12086          ling.l_linger = 0;
12087          setsockopt(_server_acception[i].recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12088          closesocket(_server_acception[i].recv_sock);
12089 
12090          if (_server_acception[i].send_sock) {
12091             setsockopt(_server_acception[i].send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12092             closesocket(_server_acception[i].send_sock);
12093          }
12094 
12095          if (_server_acception[i].event_sock) {
12096             setsockopt(_server_acception[i].event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12097             closesocket(_server_acception[i].event_sock);
12098          }
12099 
12100          _server_acception[i].recv_sock = 0;
12101          _server_acception[i].send_sock = 0;
12102          _server_acception[i].event_sock = 0;
12103       }
12104 
12105    if (_lsock) {
12106       closesocket(_lsock);
12107       _lsock = 0;
12108       _server_registered = FALSE;
12109    }
12110 
12111    /* free suspend structures */
12112    ss_suspend_exit();
12113 
12114    return RPC_SUCCESS;
12115 }
12116 
12117 
12118 /********************************************************************/
12119 INT rpc_check_channels(void)
12120 /********************************************************************\
12121 
12122   Routine: rpc_check_channels
12123 
12124   Purpose: Check open rpc channels by sending watchdog messages
12125 
12126   Input:
12127     none
12128 
12129   Output:
12130     none
12131 
12132   Function value:
12133     RPC_SUCCESS             Channel is still alive
12134     RPC_NET_ERROR           Connection is broken
12135 
12136 \********************************************************************/
12137 {
12138    INT status, idx, i, convert_flags;
12139    NET_COMMAND nc;
12140    fd_set readfds;
12141    struct timeval timeout;
12142 
12143    for (idx = 0; idx < MAX_RPC_CONNECTION; idx++) {
12144       if (_server_acception[idx].recv_sock &&
12145           _server_acception[idx].tid == ss_gettid() &&
12146           _server_acception[idx].watchdog_timeout &&
12147           (ss_millitime() - _server_acception[idx].last_activity >
12148            (DWORD) _server_acception[idx].watchdog_timeout)) {
12149 /* printf("Send watchdog message to %s on %s\n",
12150                 _server_acception[idx].prog_name,
12151                 _server_acception[idx].host_name); */
12152 
12153          /* send a watchdog message */
12154          nc.header.routine_id = MSG_WATCHDOG;
12155          nc.header.param_size = 0;
12156 
12157          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
12158          if (convert_flags) {
12159             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING, convert_flags);
12160             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING, convert_flags);
12161          }
12162 
12163          /* send the header to the client */
12164          i = send_tcp(_server_acception[idx].send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
12165 
12166          if (i < 0)
12167             goto exit;
12168 
12169          /* make some timeout checking */
12170          FD_ZERO(&readfds);
12171          FD_SET(_server_acception[idx].send_sock, &readfds);
12172          FD_SET(_server_acception[idx].recv_sock, &readfds);
12173 
12174          timeout.tv_sec = _server_acception[idx].watchdog_timeout / 1000;
12175          timeout.tv_usec = (_server_acception[idx].watchdog_timeout % 1000) * 1000;
12176 
12177          do {
12178             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12179 
12180             /* if an alarm signal was cought, restart select with reduced timeout */
12181             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12182                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12183 
12184          } while (status == -1);        /* dont return if an alarm signal was cought */
12185 
12186          if (!FD_ISSET(_server_acception[idx].send_sock, &readfds) &&
12187              !FD_ISSET(_server_acception[idx].recv_sock, &readfds))
12188             goto exit;
12189 
12190          /* receive result on send socket */
12191          if (FD_ISSET(_server_acception[idx].send_sock, &readfds)) {
12192             i = recv_tcp(_server_acception[idx].send_sock, (char *) &nc, sizeof(nc), 0);
12193             if (i <= 0)
12194                goto exit;
12195          }
12196       }
12197    }
12198 
12199    return RPC_SUCCESS;
12200 
12201  exit:
12202 
12203    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
12204           _server_acception[idx].host_name,
12205           _server_acception[idx].prog_name, _server_acception[idx].watchdog_timeout / 1000);
12206 
12207    /* disconnect from experiment */
12208    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12209       cm_disconnect_experiment();
12210 
12211    /* close server connection */
12212    if (_server_acception[idx].recv_sock)
12213       closesocket(_server_acception[idx].recv_sock);
12214    if (_server_acception[idx].send_sock)
12215       closesocket(_server_acception[idx].send_sock);
12216    if (_server_acception[idx].event_sock)
12217       closesocket(_server_acception[idx].event_sock);
12218 
12219    /* free TCP cache */
12220    M_FREE(_server_acception[idx].net_buffer);
12221    _server_acception[idx].net_buffer = NULL;
12222 
12223    /* mark this entry as invalid */
12224    memset(&_server_acception[idx], 0, sizeof(RPC_SERVER_ACCEPTION));
12225 
12226    return RPC_NET_ERROR;
12227 }
12228 
12229 /**dox***************************************************************/
12230 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12231 
12232 /**dox***************************************************************/
12233                                                                                                              /** @} *//* end of rpcfunctionc */
12234 
12235 /**dox***************************************************************/
12236 /** @addtogroup bkfunctionc
12237  *
12238  *  @{  */
12239 
12240 /********************************************************************\
12241 *                                                                    *
12242 *                 Bank functions                                     *
12243 *                                                                    *
12244 \********************************************************************/
12245 
12246 /********************************************************************/
12247 /**
12248 Initializes an event for Midas banks structure.
12249 Before banks can be created in an event, bk_init() has to be called first.
12250 @param event pointer to the area of event
12251 */
12252 void bk_init(void *event)
12253 {
12254    ((BANK_HEADER *) event)->data_size = 0;
12255    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
12256 }
12257 
12258 /**dox***************************************************************/
12259 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12260 
12261 /********************************************************************/
12262 BOOL bk_is32(void *event)
12263 /********************************************************************\
12264 
12265   Routine: bk_is32
12266 
12267   Purpose: Return true if banks inside event are 32-bit banks
12268 
12269   Input:
12270     void   *event           pointer to the event
12271 
12272   Output:
12273     none
12274 
12275   Function value:
12276     none
12277 
12278 \********************************************************************/
12279 {
12280    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12281 }
12282 
12283 /**dox***************************************************************/
12284 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12285 
12286 /********************************************************************/
12287 /**
12288 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12289 Before banks can be created in an event, bk_init32() has to be called first.
12290 @param event pointer to the area of event
12291 @return void
12292 */
12293 void bk_init32(void *event)
12294 {
12295    ((BANK_HEADER *) event)->data_size = 0;
12296    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12297 }
12298 
12299 /********************************************************************/
12300 /**
12301 Returns the size of an event containing banks.
12302 The total size of an event is the value returned by bk_size() plus the size
12303 of the event header (sizeof(EVENT_HEADER)).
12304 @param event pointer to the area of event
12305 @return number of bytes contained in data area of event
12306 */
12307 INT bk_size(void *event)
12308 {
12309    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12310 }
12311 
12312 /********************************************************************/
12313 /**
12314 Create a Midas bank.
12315 The data pointer pdata must be used as an address to fill a
12316 bank. It is incremented with every value written to the bank and finally points
12317 to a location just after the last byte of the bank. It is then passed to
12318 the function bk_close() to finish the bank creation.
12319 \code
12320 INT *pdata;
12321 bk_init(pevent);
12322 bk_create(pevent, "ADC0", TID_INT, &pdata);
12323 *pdata++ = 123
12324 *pdata++ = 456
12325 bk_close(pevent, pdata);
12326 \endcode
12327 @param event pointer to the data area
12328 @param name of the bank, must be exactly 4 charaters
12329 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12330 midas.h
12331 @param pdata pointer to the data area of the newly created bank
12332 @return void
12333 */
12334 void bk_create(void *event, const char *name, WORD type, void *pdata)
12335 {
12336    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12337       BANK32 *pbk32;
12338 
12339       pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12340       strncpy(pbk32->name, name, 4);
12341       pbk32->type = type;
12342       pbk32->data_size = 0;
12343       *((void **) pdata) = pbk32 + 1;
12344    } else {
12345       BANK *pbk;
12346 
12347       pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12348       strncpy(pbk->name, name, 4);
12349       pbk->type = type;
12350       pbk->data_size = 0;
12351       *((void **) pdata) = pbk + 1;
12352    }
12353 }
12354 
12355 
12356 
12357 /**dox***************************************************************/
12358 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12359 
12360 /********************************************************************/
12361 int bk_delete(void *event, const char *name)
12362 /********************************************************************\
12363 
12364   Routine: bk_delete
12365 
12366   Purpose: Delete a MIDAS bank inside an event
12367 
12368   Input:
12369     void   *event           pointer to the event
12370     char   *name            Name of bank (exactly four letters)
12371 
12372   Function value:
12373     CM_SUCCESS              Bank has been deleted
12374     0                       Bank has not been found
12375 
12376 \********************************************************************/
12377 {
12378    BANK *pbk;
12379    BANK32 *pbk32;
12380    DWORD dname;
12381    int remaining;
12382 
12383    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12384       /* locate bank */
12385       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12386       strncpy((char *) &dname, name, 4);
12387       do {
12388          if (*((DWORD *) pbk32->name) == dname) {
12389             /* bank found, delete it */
12390             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
12391                          sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12392 
12393             /* reduce total event size */
12394             ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
12395 
12396             /* copy remaining bytes */
12397             if (remaining > 0)
12398                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12399             return CM_SUCCESS;
12400          }
12401 
12402          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12403       } while ((DWORD) ((char *) pbk32 - (char *) event) <
12404                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12405    } else {
12406       /* locate bank */
12407       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12408       strncpy((char *) &dname, name, 4);
12409       do {
12410          if (*((DWORD *) pbk->name) == dname) {
12411             /* bank found, delete it */
12412             remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
12413                          sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12414 
12415             /* reduce total event size */
12416             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
12417 
12418             /* copy remaining bytes */
12419             if (remaining > 0)
12420                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
12421             return CM_SUCCESS;
12422          }
12423 
12424          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12425       } while ((DWORD) ((char *) pbk - (char *) event) <
12426                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12427    }
12428 
12429    return 0;
12430 }
12431 
12432 /**dox***************************************************************/
12433 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12434 
12435 /********************************************************************/
12436 /**
12437 Close the Midas bank priviously created by bk_create().
12438 The data pointer pdata must be obtained by bk_create() and
12439 used as an address to fill a bank. It is incremented with every value written
12440 to the bank and finally points to a location just after the last byte of the
12441 bank. It is then passed to bk_close() to finish the bank creation
12442 @param event pointer to current composed event
12443 @param pdata  pointer to the data
12444 @return number of bytes contained in bank
12445 */
12446 INT bk_close(void *event, void *pdata)
12447 {
12448    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12449       BANK32 *pbk32;
12450 
12451       pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12452       pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
12453       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12454          printf("Warning: bank %c%c%c%c has zero size\n",
12455                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
12456       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
12457       return pbk32->data_size;
12458    } else {
12459       BANK *pbk;
12460 
12461       pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
12462       pbk->data_size = (WORD) ((char *) pdata - (char *) (pbk + 1));
12463       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12464          printf("Warning: bank %c%c%c%c has zero size\n",
12465                 pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12466       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
12467       return pbk->data_size;
12468    }
12469 }
12470 
12471 /********************************************************************/
12472 /**
12473 Extract the MIDAS bank name listing of an event.
12474 The bklist should be dimensioned with STRING_BANKLIST_MAX
12475 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12476 \code
12477 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12478 {
12479   INT    n_adc, nbanks;
12480   WORD   *pdata;
12481   char   banklist[STRING_BANKLIST_MAX];
12482 
12483   // Display # of banks and list of banks in the event
12484   nbanks = bk_list(pevent, banklist);
12485   printf("#banks:%d List:%s\n", nbanks, banklist);
12486 
12487   // look for ADC0 bank, return if not present
12488   n_adc = bk_locate(pevent, "ADC0", &pdata);
12489   ...
12490 }
12491 \endcode
12492 @param event pointer to current composed event
12493 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12494 @return number of bank found in this event.
12495 */
12496 INT bk_list(void *event, char *bklist)
12497 {                               /* Full event */
12498    INT nbk, size;
12499    BANK *pmbk = NULL;
12500    BANK32 *pmbk32 = NULL;
12501    char *pdata;
12502 
12503    /* compose bank list */
12504    bklist[0] = 0;
12505    nbk = 0;
12506    do {
12507       /* scan all banks for bank name only */
12508       if (bk_is32(event)) {
12509          size = bk_iterate32(event, &pmbk32, &pdata);
12510          if (pmbk32 == NULL)
12511             break;
12512       } else {
12513          size = bk_iterate(event, &pmbk, &pdata);
12514          if (pmbk == NULL)
12515             break;
12516       }
12517       nbk++;
12518 
12519       if (nbk > BANKLIST_MAX) {
12520          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
12521          return (nbk - 1);
12522       }
12523       if (bk_is32(event))
12524          strncat(bklist, (char *) pmbk32->name, 4);
12525       else
12526          strncat(bklist, (char *) pmbk->name, 4);
12527    }
12528    while (1);
12529    return (nbk);
12530 }
12531 
12532 /********************************************************************/
12533 /**
12534 Locates a MIDAS bank of given name inside an event.
12535 @param event pointer to current composed event
12536 @param name bank name to look for
12537 @param pdata pointer to data area of bank, NULL if bank not found
12538 @return number of values inside the bank
12539 */
12540 INT bk_locate(void *event, const char *name, void *pdata)
12541 {
12542    BANK *pbk;
12543    BANK32 *pbk32;
12544    DWORD dname;
12545 
12546    if (bk_is32(event)) {
12547       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12548       strncpy((char *) &dname, name, 4);
12549       while ((DWORD) ((char *) pbk32 - (char *) event) <
12550              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12551          if (*((DWORD *) pbk32->name) == dname) {
12552             *((void **) pdata) = pbk32 + 1;
12553             if (tid_size[pbk32->type & 0xFF] == 0)
12554                return pbk32->data_size;
12555             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
12556          }
12557          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12558       }
12559    } else {
12560       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12561       strncpy((char *) &dname, name, 4);
12562       while ((DWORD) ((char *) pbk - (char *) event) <
12563              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12564          if (*((DWORD *) pbk->name) == dname) {
12565             *((void **) pdata) = pbk + 1;
12566             if (tid_size[pbk->type & 0xFF] == 0)
12567                return pbk->data_size;
12568             return pbk->data_size / tid_size[pbk->type & 0xFF];
12569          }
12570          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12571       }
12572 
12573    }
12574 
12575    /* bank not found */
12576    *((void **) pdata) = NULL;
12577    return 0;
12578 }
12579 
12580 /********************************************************************/
12581 /**
12582 Finds a MIDAS bank of given name inside an event.
12583 @param pbkh pointer to current composed event
12584 @param name bank name to look for
12585 @param bklen number of elemtents in bank
12586 @param bktype bank type, one of TID_xxx
12587 @param pdata pointer to data area of bank, NULL if bank not found
12588 @return 1 if bank found, 0 otherwise
12589 */
12590 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype, void **pdata)
12591 {
12592    BANK *pbk;
12593    BANK32 *pbk32;
12594    DWORD dname;
12595 
12596    if (bk_is32(pbkh)) {
12597       pbk32 = (BANK32 *) (pbkh + 1);
12598       strncpy((char *) &dname, name, 4);
12599       do {
12600          if (*((DWORD *) pbk32->name) == dname) {
12601             *((void **) pdata) = pbk32 + 1;
12602             if (tid_size[pbk32->type & 0xFF] == 0)
12603                *bklen = pbk32->data_size;
12604             else
12605                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
12606 
12607             *bktype = pbk32->type;
12608             return 1;
12609          }
12610          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12611       } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
12612    } else {
12613       pbk = (BANK *) (pbkh + 1);
12614       strncpy((char *) &dname, name, 4);
12615       do {
12616          if (*((DWORD *) pbk->name) == dname) {
12617             *((void **) pdata) = pbk + 1;
12618             if (tid_size[pbk->type & 0xFF] == 0)
12619                *bklen = pbk->data_size;
12620             else
12621                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
12622 
12623             *bktype = pbk->type;
12624             return 1;
12625          }
12626          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12627       } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
12628    }
12629 
12630    /* bank not found */
12631    *((void **) pdata) = NULL;
12632    return 0;
12633 }
12634 
12635 /********************************************************************/
12636 /**
12637 Iterates through banks inside an event.
12638 The function can be used to enumerate all banks of an event.
12639 The returned pointer to the bank header has following structure:
12640 \code
12641 typedef struct {
12642 char   name[4];
12643 WORD   type;
12644 WORD   data_size;
12645 } BANK;
12646 \endcode
12647 where type is a TID_xxx value and data_size the size of the bank in bytes.
12648 \code
12649 BANK *pbk;
12650 INT  size;
12651 void *pdata;
12652 char name[5];
12653 pbk = NULL;
12654 do
12655 {
12656  size = bk_iterate(event, &pbk, &pdata);
12657  if (pbk == NULL)
12658   break;
12659  *((DWORD *)name) = *((DWORD *)(pbk)->name);
12660  name[4] = 0;
12661  printf("bank %s found\n", name);
12662 } while(TRUE);
12663 \endcode
12664 @param event Pointer to data area of event.
12665 @param pbk pointer to the bank header, must be NULL for the first call to
12666 this function.
12667 @param pdata Pointer to the bank header, must be NULL for the first
12668 call to this function
12669 @return Size of bank in bytes
12670 */
12671 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
12672 {
12673    if (*pbk == NULL)
12674       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12675    else
12676       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12677 
12678    *((void **) pdata) = (*pbk) + 1;
12679 
12680    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12681       *pbk = *((BANK **) pdata) = NULL;
12682       return 0;
12683    }
12684 
12685    return (*pbk)->data_size;
12686 }
12687 
12688 
12689 /**dox***************************************************************/
12690 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12691 
12692 /********************************************************************/
12693 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
12694 /********************************************************************\
12695 
12696   Routine: bk_iterate
12697 
12698   Purpose: Iterate through 32 bit MIDAS banks inside an event
12699 
12700   Input:
12701     void   *event           pointer to the event
12702     BANK   **pbk32          must be NULL for the first call to bk_iterate
12703 
12704   Output:
12705     BANK   **pbk            pointer to the bank header
12706     void   *pdata           pointer to data area of the bank
12707 
12708   Function value:
12709     INT    size of the bank in bytes
12710 
12711 \********************************************************************/
12712 {
12713    if (*pbk == NULL)
12714       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
12715    else
12716       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12717 
12718    *((void **) pdata) = (*pbk) + 1;
12719 
12720    if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12721       *pbk = *((BANK32 **) pdata) = NULL;
12722       return 0;
12723    }
12724 
12725    return (*pbk)->data_size;
12726 }
12727 
12728 /**dox***************************************************************/
12729 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12730 
12731 /********************************************************************/
12732 /**
12733 Swaps bytes from little endian to big endian or vice versa for a whole event.
12734 
12735 An event contains a flag which is set by bk_init() to identify
12736 the endian format of an event. If force is FALSE, this flag is evaluated and
12737 the event is only swapped if it is in the "wrong" format for this system.
12738 An event can be swapped to the "wrong" format on purpose for example by a
12739 front-end which wants to produce events in a "right" format for a back-end
12740 analyzer which has different byte ordering.
12741 @param event pointer to data area of event
12742 @param force If TRUE, the event is always swapped, if FALSE, the event
12743 is only swapped if it is in the wrong format.
12744 @return 1==event has been swap, 0==event has not been swapped.
12745 */
12746 INT bk_swap(void *event, BOOL force)
12747 {
12748    BANK_HEADER *pbh;
12749    BANK *pbk;
12750    BANK32 *pbk32;
12751    void *pdata;
12752    WORD type;
12753    BOOL b32;
12754 
12755    pbh = (BANK_HEADER *) event;
12756 
12757    /* only swap if flags in high 16-bit */
12758    if (pbh->flags < 0x10000 && !force)
12759       return 0;
12760 
12761    /* swap bank header */
12762    DWORD_SWAP(&pbh->data_size);
12763    DWORD_SWAP(&pbh->flags);
12764 
12765    /* check for 32bit banks */
12766    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
12767 
12768    pbk = (BANK *) (pbh + 1);
12769    pbk32 = (BANK32 *) pbk;
12770 
12771    /* scan event */
12772    while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
12773       /* swap bank header */
12774       if (b32) {
12775          DWORD_SWAP(&pbk32->type);
12776          DWORD_SWAP(&pbk32->data_size);
12777          pdata = pbk32 + 1;
12778          type = (WORD) pbk32->type;
12779       } else {
12780          WORD_SWAP(&pbk->type);
12781          WORD_SWAP(&pbk->data_size);
12782          pdata = pbk + 1;
12783          type = pbk->type;
12784       }
12785 
12786       /* pbk points to next bank */
12787       if (b32) {
12788          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12789          pbk = (BANK *) pbk32;
12790       } else {
12791          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12792          pbk32 = (BANK32 *) pbk;
12793       }
12794 
12795       switch (type) {
12796       case TID_WORD:
12797       case TID_SHORT:
12798          while ((char *) pdata < (char *) pbk) {
12799             WORD_SWAP(pdata);
12800             pdata = (void *) (((WORD *) pdata) + 1);
12801          }
12802          break;
12803 
12804       case TID_DWORD:
12805       case TID_INT:
12806       case TID_BOOL:
12807       case TID_FLOAT:
12808          while ((char *) pdata < (char *) pbk) {
12809             DWORD_SWAP(pdata);
12810             pdata = (void *) (((DWORD *) pdata) + 1);
12811          }
12812          break;
12813 
12814       case TID_DOUBLE:
12815          while ((char *) pdata < (char *) pbk) {
12816             QWORD_SWAP(pdata);
12817             pdata = (void *) (((double *) pdata) + 1);
12818          }
12819          break;
12820       }
12821    }
12822 
12823    return CM_SUCCESS;
12824 }
12825 
12826 /**dox***************************************************************/
12827                                                                                                              /** @} *//* end of bkfunctionc */
12828 
12829 /***** sKIP eb_xxx **************************************************/
12830 /**dox***************************************************************/
12831 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12832 /***** sKIP eb_xxx **************************************************/
12833 
12834 #if !defined(OS_VXWORKS)
12835 /********************************************************************\
12836 *                                                                    *
12837 *                 Event buffer functions                             *
12838 *                                                                    *
12839 \********************************************************************/
12840 
12841 /* PAA several modification in the eb_xxx()
12842    also new function eb_buffer_full()
12843 */
12844 static char *_event_ring_buffer = NULL;
12845 static INT _eb_size;
12846 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
12847 
12848 /********************************************************************/
12849 INT eb_create_buffer(INT size)
12850 /********************************************************************\
12851 
12852   Routine: eb_create_buffer
12853 
12854   Purpose: Create an event buffer. Has to be called initially before
12855            any other eb_xxx function
12856 
12857   Input:
12858     INT    size             Size in bytes
12859 
12860   Output:
12861     none
12862 
12863   Function value:
12864     CM_SUCCESS              Successful completion
12865     BM_NO_MEMEORY           Out of memory
12866 
12867 \********************************************************************/
12868 {
12869    _event_ring_buffer = (char *) M_MALLOC(size);
12870    if (_event_ring_buffer == NULL)
12871       return BM_NO_MEMORY;
12872 
12873    memset(_event_ring_buffer, 0, size);
12874    _eb_size = size;
12875 
12876    _eb_write_pointer = _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
12877 
12878    _send_sock = rpc_get_event_sock();
12879 
12880    return CM_SUCCESS;
12881 }
12882 
12883 /********************************************************************/
12884 INT eb_free_buffer()
12885 /********************************************************************\
12886 
12887   Routine: eb_free_buffer
12888 
12889   Purpose: Free memory allocated voa eb_create_buffer
12890 
12891   Input:
12892     none
12893 
12894   Output:
12895     none
12896 
12897   Function value:
12898     CM_SUCCESS              Successful completion
12899 
12900 \********************************************************************/
12901 {
12902    if (_event_ring_buffer)
12903       M_FREE(_event_ring_buffer);
12904 
12905    _eb_size = 0;
12906    return CM_SUCCESS;
12907 }
12908 
12909 
12910 /********************************************************************/
12911 INT eb_free_space(void)
12912 /********************************************************************\
12913 
12914   Routine: eb_free_space
12915 
12916   Purpose: Compute and return usable free space in the event buffer
12917 
12918   Input:
12919     none
12920 
12921   Output:
12922     none
12923 
12924   Function value:
12925     INT    Number of usable free bytes in the event buffer
12926 
12927 \********************************************************************/
12928 {
12929    INT free_space;
12930 
12931    if (_event_ring_buffer == NULL) {
12932       cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
12933       return -1;
12934    }
12935 
12936    if (_eb_write_pointer >= _eb_read_pointer) {
12937       free_space = _eb_size - ((POINTER_T) _eb_write_pointer - (POINTER_T) _event_ring_buffer);
12938    } else if (_eb_write_pointer >= _event_ring_buffer) {
12939       free_space = (POINTER_T) _eb_read_pointer - (POINTER_T) _eb_write_pointer;
12940    } else if (_eb_end_pointer == _event_ring_buffer) {
12941       _eb_write_pointer = _event_ring_buffer;
12942       free_space = _eb_size;
12943    } else if (_eb_read_pointer == _event_ring_buffer) {
12944       free_space = 0;
12945    } else {
12946       _eb_write_pointer = _event_ring_buffer;
12947       free_space = (POINTER_T) _eb_read_pointer - (POINTER_T) _eb_write_pointer;
12948    }
12949 
12950    return free_space;
12951 }
12952 
12953 
12954 /********************************************************************/
12955 DWORD eb_get_level()
12956 /********************************************************************\
12957 
12958   Routine: eb_get_level
12959 
12960   Purpose: Return filling level of event buffer in percent
12961 
12962   Input:
12963     none
12964 
12965   Output:
12966     none
12967 
12968   Function value:
12969     DWORD level              0..99
12970 
12971 \********************************************************************/
12972 {
12973    INT size;
12974 
12975    size = _eb_size - eb_free_space();
12976 
12977    return (100 * size) / _eb_size;
12978 }
12979 
12980 
12981 /********************************************************************/
12982 BOOL eb_buffer_full(void)
12983 /********************************************************************\
12984 
12985   Routine: eb_buffer_full
12986 
12987   Purpose: Test if there is sufficient space in the event buffer
12988     for another event
12989 
12990   Input:
12991     none
12992 
12993   Output:
12994     none
12995 
12996   Function value:
12997     BOOL  Is there enough space for another event in the event buffer
12998 
12999 \********************************************************************/
13000 {
13001    DWORD free_space;
13002 
13003    free_space = eb_free_space();
13004 
13005    /* if max. event won't fit, return zero */
13006    return (free_space < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
13007 }
13008 
13009 
13010 /********************************************************************/
13011 EVENT_HEADER *eb_get_pointer()
13012 /********************************************************************\
13013 
13014   Routine: eb_get_pointer
13015 
13016   Purpose: Get pointer to next free location in event buffer
13017 
13018   Input:
13019     none
13020 
13021   Output:
13022     none
13023 
13024   Function value:
13025     EVENT_HEADER *            Pointer to free location
13026 
13027 \********************************************************************/
13028 {
13029    /* if max. event won't fit, return zero */
13030    if (eb_buffer_full()) {
13031 #ifdef OS_VXWORKS
13032       logMsg("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
13033              _eb_read_pointer - _event_ring_buffer,
13034              _eb_write_pointer - _event_ring_buffer, _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
13035 #endif
13036       return NULL;
13037    }
13038 
13039    /* leave space for buffer handle */
13040    return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
13041 }
13042 
13043 
13044 /********************************************************************/
13045 INT eb_increment_pointer(INT buffer_handle, INT event_size)
13046 /********************************************************************\
13047 
13048   Routine: eb_increment_pointer
13049 
13050   Purpose: Increment write pointer of event buffer after an event
13051            has been copied into the buffer (at an address previously
13052            obtained via eb_get_pointer)
13053 
13054   Input:
13055     INT buffer_handle         Buffer handle event should be sent to
13056     INT event_size            Event size in bytes including header
13057 
13058   Output:
13059     none
13060 
13061   Function value:
13062     CM_SUCCESS                Successful completion
13063 
13064 \********************************************************************/
13065 {
13066    INT aligned_event_size;
13067 
13068    /* if not connected remotely, use bm_send_event */
13069    if (_send_sock == 0)
13070       return bm_send_event(buffer_handle, _eb_write_pointer + sizeof(INT), event_size, SYNC);
13071 
13072    aligned_event_size = ALIGN8(event_size);
13073 
13074    /* copy buffer handle */
13075    *((INT *) _eb_write_pointer) = buffer_handle;
13076    _eb_write_pointer += sizeof(INT) + aligned_event_size;
13077 
13078    if (_eb_write_pointer > _eb_end_pointer)
13079       _eb_end_pointer = _eb_write_pointer;
13080 
13081    if (_eb_write_pointer > _event_ring_buffer + _eb_size)
13082       cm_msg(MERROR, "eb_increment_pointer",
13083              "event size (%d) exeeds maximum event size (%d)", event_size, MAX_EVENT_SIZE);
13084 
13085    if (_eb_size - ((POINTER_T) _eb_write_pointer - (POINTER_T) _event_ring_buffer) <
13086        (int)(MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT))) {
13087       _eb_write_pointer = _event_ring_buffer;
13088 
13089       /* avoid rp==wp */
13090       if (_eb_read_pointer == _event_ring_buffer)
13091          _eb_write_pointer--;
13092    }
13093 
13094    return CM_SUCCESS;
13095 }
13096 
13097 
13098 /********************************************************************/
13099 INT eb_send_events(BOOL send_all)
13100 /********************************************************************\
13101 
13102   Routine: eb_send_events
13103 
13104   Purpose: Send events from the event buffer to the server
13105 
13106   Input:
13107     BOOL send_all             If FALSE, only send events if buffer
13108                               contains more than _opt_tcp_size bytes
13109 
13110   Output:
13111     none
13112 
13113   Function value:
13114     CM_SUCCESS                Successful completion
13115 
13116 \********************************************************************/
13117 {
13118    char *eb_wp, *eb_ep;
13119    INT size, i;
13120 
13121    /* write pointers are volatile, so make copy */
13122    eb_ep = _eb_end_pointer;
13123    eb_wp = _eb_write_pointer;
13124 
13125    if (eb_wp == _eb_read_pointer)
13126       return CM_SUCCESS;
13127    if (eb_wp > _eb_read_pointer) {
13128       size = (POINTER_T) eb_wp - (POINTER_T) _eb_read_pointer;
13129 
13130       /* don't send if less than optimal TCP buffer size available */
13131       if (size < (INT) _opt_tcp_size && !send_all)
13132          return CM_SUCCESS;
13133    } else {
13134       /* send last piece of event buffer */
13135       size = (POINTER_T) eb_ep - (POINTER_T) _eb_read_pointer;
13136    }
13137 
13138    while (size > _opt_tcp_size) {
13139       /* send buffer */
13140       i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
13141       if (i < 0) {
13142          printf("send_tcp() returned %d\n", i);
13143          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
13144          return RPC_NET_ERROR;
13145       }
13146 
13147       _eb_read_pointer += _opt_tcp_size;
13148       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
13149          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
13150 
13151       size -= _opt_tcp_size;
13152    }
13153 
13154    if (send_all || eb_wp < _eb_read_pointer) {
13155       /* send buffer */
13156       i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
13157       if (i < 0) {
13158          printf("send_tcp() returned %d\n", i);
13159          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
13160          return RPC_NET_ERROR;
13161       }
13162 
13163       _eb_read_pointer += size;
13164       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
13165          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
13166    }
13167 
13168    /* Check for case where eb_wp = eb_ring_buffer - 1 */
13169    if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
13170       return CM_SUCCESS;
13171    }
13172 
13173    if (eb_wp != _eb_read_pointer)
13174       return BM_MORE_EVENTS;
13175 
13176    return CM_SUCCESS;
13177 }
13178 
13179 #endif                          /* OS_VXWORKS  eb section */
13180 
13181 /**dox***************************************************************/
13182 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13183 
13184 /**dox***************************************************************/
13185 /** @addtogroup dmfunctionc
13186  *
13187  *  @{  */
13188 
13189 /**dox***************************************************************/
13190 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13191 
13192 /********************************************************************\
13193 *                                                                    *
13194 *                 Dual memory buffer functions                       *
13195 *                                                                    *
13196 * Provide a dual memory buffer scheme for handling front-end         *
13197 * event. This code as been requested for allowing contemporary       *
13198 * task handling a)acquisition, b)network transfer if possible.       *
13199 * The pre-compiler switch will determine the mode of operation.      *
13200 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
13201 * a seperate task taking care of the dm_area_send                    *
13202 *                                                                    *
13203 * "*" : visible functions                                            *
13204 * dm_buffer_create():     *Setup the dual memory buffer              *
13205 *                          Setup semaphore                           *
13206 *                          Spawn second thread                       *
13207 * dm_buffer_release():    *Release memory allocation for dm          *
13208 *                          Force a kill of 2nd thread                *
13209 *                          Remove semaphore                          *
13210 * dm_area_full():         *Check for both area being full            *
13211 *                          None blocking, may be used for interrupt  *
13212 *                          disable.                                  *
13213 * dm_pointer_get()     :  *Check memory space and return pointer     *
13214 *                          Blocking function with timeout if no more *
13215 *                          space for next event. If error will abort.*
13216 * dm_pointer_increment(): *Move pointer to next free location        *
13217 *                          None blocking. performs bm_send_event if  *
13218 *                          local connection.                         *
13219 * dm_area_send():         *Transfer FULL buffer(s)                   *
13220 *                          None blocking function.                   *
13221 *                          if DUAL_THREAD: Give sem_send semaphore   *
13222 *                          else transfer FULL buffer                 *
13223 * dm_area_flush():        *Transfer all remaining events from dm     *
13224 *                          Blocking function with timeout            *
13225 *                          if DUAL_THREAD: Give sem_flush semaphore. *
13226 * dm_task():               Secondary thread handling DUAL_THREAD     *
13227 *                          mechanism. Serves 2 requests:             *
13228 *                          dm_send:  Transfer FULL buffer only.      *
13229 *                          dm_flush: Transfer ALL buffers.           *
13230 * dm_area_switch():        internal, used by dm_pointer_get()        *
13231 * dm_active_full():        internal: check space in current buffer   *
13232 * dm_buffer_send():        internal: send data for given area        *
13233 * dm_buffer_time_get():    interal: return the time stamp of the     *
13234 *                          last switch                               *
13235 \********************************************************************/
13236 
13237 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
13238 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
13239 #define DM_KILL        12       /* Kill request for 2nd thread */
13240 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
13241 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
13242 
13243 typedef struct {
13244    char *pt;                    /* top pointer    memory buffer          */
13245    char *pw;                    /* write pointer  memory buffer          */
13246    char *pe;                    /* end   pointer  memory buffer          */
13247    char *pb;                    /* bottom pointer memory buffer          */
13248    BOOL full;                   /* TRUE if memory buffer is full         */
13249    DWORD serial;                /* full buffer serial# for evt order     */
13250 } DMEM_AREA;
13251 
13252 typedef struct {
13253    DMEM_AREA *pa;               /* active memory buffer */
13254    DMEM_AREA area1;             /* mem buffer area 1 */
13255    DMEM_AREA area2;             /* mem buffer area 2 */
13256    DWORD serial;                /* overall buffer serial# for evt order     */
13257    INT action;                  /* for multi thread configuration */
13258    DWORD last_active;           /* switch time stamp */
13259    HNDLE sem_send;              /* semaphore for dm_task */
13260    HNDLE sem_flush;             /* semaphore for dm_task */
13261 } DMEM_BUFFER;
13262 
13263 DMEM_BUFFER dm;
13264 INT dm_user_max_event_size;
13265 
13266 /**dox***************************************************************/
13267 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13268 
13269 /********************************************************************/
13270 /**
13271 Setup a dual memory buffer. Has to be called initially before
13272            any other dm_xxx function
13273 @param size             Size in bytes
13274 @param user_max_event_size max event size
13275 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
13276 */
13277 INT dm_buffer_create(INT size, INT user_max_event_size)
13278 {
13279 
13280    dm.area1.pt = (char *) M_MALLOC(size);
13281    if (dm.area1.pt == NULL)
13282       return (BM_NO_MEMORY);
13283    dm.area2.pt = (char *) M_MALLOC(size);
13284    if (dm.area2.pt == NULL)
13285       return (BM_NO_MEMORY);
13286 
13287    /* check user event size against the system MAX_EVENT_SIZE */
13288    if (user_max_event_size > MAX_EVENT_SIZE) {
13289       cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
13290       return BM_MEMSIZE_MISMATCH;
13291    }
13292    dm_user_max_event_size = user_max_event_size;
13293 
13294    memset(dm.area1.pt, 0, size);
13295    memset(dm.area2.pt, 0, size);
13296 
13297    /* initialize pointers */
13298    dm.area1.pb = dm.area1.pt + size - 1024;
13299    dm.area1.pw = dm.area1.pe = dm.area1.pt;
13300    dm.area2.pb = dm.area2.pt + size - 1024;
13301    dm.area2.pw = dm.area2.pe = dm.area2.pt;
13302 
13303   /*-PAA-*/
13304 #ifdef DM_DEBUG
13305    printf(" in dm_buffer_create ---------------------------------\n");
13306    printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw, dm.area1.pe, dm.area1.pb);
13307    printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw, dm.area2.pe, dm.area2.pb);
13308 #endif
13309 
13310    /* activate first area */
13311    dm.pa = &dm.area1;
13312 
13313    /* Default not full */
13314    dm.area1.full = dm.area2.full = FALSE;
13315 
13316    /* Reset serial buffer number with proper starting sequence */
13317    dm.area1.serial = dm.area2.serial = 0;
13318    /* ensure proper serial on next increment */
13319    dm.serial = 1;
13320 
13321    /* set active buffer time stamp */
13322    dm.last_active = ss_millitime();
13323 
13324    /* get socket for event sending */
13325    _send_sock = rpc_get_event_sock();
13326 
13327 #ifdef DM_DUAL_THREAD
13328    {
13329       INT status;
13330       VX_TASK_SPAWN starg;
13331 
13332       /* create semaphore */
13333       status = ss_mutex_create("send", &dm.sem_send);
13334       if (status != SS_CREATED && status != SS_SUCCESS) {
13335          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
13336          return status;
13337       }
13338       status = ss_mutex_create("flush", &dm.sem_flush);
13339       if (status != SS_CREATED && status != SS_SUCCESS) {
13340          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
13341          return status;
13342       }
13343       /* spawn dm_task */
13344       memset(&starg, 0, sizeof(VX_TASK_SPAWN));
13345 
13346 #ifdef OS_VXWORKS
13347       /* Fill up the necessary arguments */
13348       strcpy(starg.name, "areaSend");
13349       starg.priority = 120;
13350       starg.stackSize = 20000;
13351 #endif
13352 
13353       if ((status = ss_thread_create(dm_task, (void *) &starg))
13354           != SS_SUCCESS) {
13355          cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
13356          return status;
13357       }
13358 #ifdef OS_WINNT
13359       /* necessary for true MUTEX (NT) */
13360       ss_mutex_wait_for(dm.sem_send, 0);
13361 #endif
13362    }
13363 #endif                          /* DM_DUAL_THREAD */
13364 
13365    return CM_SUCCESS;
13366 }
13367 
13368 /**dox***************************************************************/
13369 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13370 
13371 /********************************************************************/
13372 INT dm_buffer_release(void)
13373 /********************************************************************\
13374   Routine: dm_buffer_release
13375 
13376   Purpose: Release dual memory buffers
13377   Input:
13378     none
13379   Output:
13380     none
13381   Function value:
13382     CM_SUCCESS              Successful completion
13383 \********************************************************************/
13384 {
13385    if (dm.area1.pt) {
13386       free(dm.area1.pt);
13387       dm.area1.pt = NULL;
13388    }
13389    if (dm.area2.pt) {
13390       free(dm.area2.pt);
13391       dm.area2.pt = NULL;
13392    }
13393    dm.serial = 0;
13394    dm.area1.full = dm.area2.full = TRUE;
13395    dm.area1.serial = dm.area2.serial = 0;
13396 
13397 #ifdef DM_DUAL_THREAD
13398    /* kill spawned dm_task */
13399    dm.action = DM_KILL;
13400    ss_mutex_release(dm.sem_send);
13401    ss_mutex_release(dm.sem_flush);
13402 
13403    /* release semaphore */
13404    ss_mutex_delete(dm.sem_send, 0);
13405    ss_mutex_delete(dm.sem_flush, 0);
13406 #endif
13407 
13408    return CM_SUCCESS;
13409 }
13410 
13411 /********************************************************************/
13412 INLINE DMEM_AREA *dm_area_switch(void)
13413 /********************************************************************\
13414   Routine: dm_area_switch
13415 
13416   Purpose: set active area to the other empty area or NULL if both
13417            area are full. May have to check the serial consistancy...
13418   Input:
13419     none
13420   Output:
13421     none
13422   Function value:
13423     DMEM_AREA *            Pointer to active area or both full
13424 \********************************************************************/
13425 {
13426    volatile BOOL full1, full2;
13427 
13428    full1 = dm.area1.full;
13429    full2 = dm.area2.full;
13430 
13431    if (!full1 && !full2) {
13432       if (dm.area1.serial <= dm.area2.serial)
13433          return (&(dm.area1));
13434       else
13435          return (&(dm.area2));
13436    }
13437 
13438    if (!full1) {
13439       return (&(dm.area1));
13440    } else if (!full2) {
13441       return (&(dm.area2));
13442    }
13443    return (NULL);
13444 }
13445 
13446 /********************************************************************/
13447 INLINE BOOL dm_area_full(void)
13448 /********************************************************************\
13449   Routine: dm_area_full
13450 
13451   Purpose: Test if both area are full in order to block interrupt
13452   Input:
13453     none
13454   Output:
13455     none
13456   Function value:
13457     BOOL         TRUE if not enough space for another event
13458 \********************************************************************/
13459 {
13460    if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
13461       return TRUE;
13462    return FALSE;
13463 }
13464 
13465 /********************************************************************/
13466 INLINE BOOL dm_active_full(void)
13467 /********************************************************************\
13468   Routine: dm_active_full
13469 
13470   Purpose: Test if there is sufficient space in either event buffer
13471            for another event.
13472   Input:
13473     none
13474   Output:
13475     none
13476   Function value:
13477     BOOL         TRUE if not enough space for another event
13478 \********************************************************************/
13479 {
13480    /* catch both full areas, waiting for transfer */
13481    if (dm.pa == NULL)
13482       return TRUE;
13483    /* Check the space in the active buffer only
13484       as I don't switch buffer here */
13485    if (dm.pa->full)
13486       return TRUE;
13487    return (((POINTER_T) dm.pa->pb - (POINTER_T) dm.pa->pw) < (INT)
13488            (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
13489 }
13490 
13491 /********************************************************************/
13492 DWORD dm_buffer_time_get(void)
13493 /********************************************************************\
13494   Routine: dm_buffer_time_get
13495 
13496   Purpose: return the time from the last buffer switch.
13497 
13498   Input:
13499     none
13500   Output:
13501     none
13502   Function value:
13503     DWORD        time stamp
13504 
13505 \********************************************************************/
13506 {
13507    return (dm.last_active);
13508 }
13509 
13510 
13511 /********************************************************************/
13512 EVENT_HEADER *dm_pointer_get(void)
13513 /********************************************************************\
13514   Routine: dm_pointer_get
13515 
13516   Purpose: Get pointer to next free location in event buffer.
13517            after 10sec tries, it times out return NULL indicating a
13518            serious problem, i.e. abort.
13519   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
13520   Input:
13521     none
13522   Output:
13523     DM_BUFFER * dm    local valid dm to work on
13524   Function value:
13525     EVENT_HEADER *    Pointer to free location
13526     NULL              cannot after several attempt get free space => abort
13527 \********************************************************************/
13528 {
13529    int timeout, status;
13530 
13531    /* Is there still space in the active area ? */
13532    if (!dm_active_full())
13533       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
13534 
13535    /* no more space => switch area */
13536 
13537    /* Tag current area with global dm.serial for order consistency */
13538    dm.pa->serial = dm.serial++;
13539 
13540    /* set active buffer time stamp */
13541    dm.last_active = ss_millitime();
13542 
13543    /* mark current area full */
13544    dm.pa->full = TRUE;
13545 
13546    /* Trigger/do data transfer (Now/don't wait) */
13547    if ((status = dm_area_send()) == RPC_NET_ERROR) {
13548       cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
13549       return NULL;
13550    }
13551 
13552    /* wait switch completion (max 10 sec) */
13553    timeout = ss_millitime();    /* with timeout */
13554    while ((ss_millitime() - timeout) < 10000) {
13555       dm.pa = dm_area_switch();
13556       if (dm.pa != NULL)
13557          return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
13558       ss_sleep(200);
13559 #ifdef DM_DEBUG
13560       printf(" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
13561              ss_millitime() - timeout, dm.area1.full, dm.area2.full, dm.area1.serial,
13562              dm.area2.serial, dm.serial);
13563 #endif
13564    }
13565 
13566    /* Time running out abort */
13567    cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
13568    return NULL;
13569 }
13570 
13571 
13572 /********************************************************************/
13573 int dm_pointer_increment(INT buffer_handle, INT event_size)
13574 /********************************************************************\
13575   Routine: dm_pointer_increment
13576 
13577   Purpose: Increment write pointer of event buffer after an event
13578            has been copied into the buffer (at an address previously
13579            obtained via dm_pointer_get)
13580   Input:
13581     INT buffer_handle         Buffer handle event should be sent to
13582     INT event_size            Event size in bytes including header
13583   Output:
13584     none
13585   Function value:
13586     CM_SUCCESS                Successful completion
13587     status                    from bm_send_event for local connection
13588 \********************************************************************/
13589 {
13590    INT aligned_event_size;
13591 
13592    /* if not connected remotely, use bm_send_event */
13593    if (_send_sock == 0) {
13594       *((INT *) dm.pa->pw) = buffer_handle;
13595       return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT), event_size, SYNC);
13596    }
13597    aligned_event_size = ALIGN8(event_size);
13598 
13599    *((INT *) dm.pa->pw) = buffer_handle;
13600 
13601    /* adjust write pointer */
13602    dm.pa->pw += sizeof(INT) + aligned_event_size;
13603 
13604    /* adjust end pointer */
13605    dm.pa->pe = dm.pa->pw;
13606 
13607    return CM_SUCCESS;
13608 }
13609 
13610 /********************************************************************/
13611 INLINE INT dm_buffer_send(DMEM_AREA * larea)
13612 /********************************************************************\
13613   Routine: dm_buffer_send
13614 
13615   Purpose: Ship data to the cache in fact!
13616            Basically the same do loop is done in the send_tcp.
13617            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
13618            Introduced for bringing tcp option to user code.
13619   Input:
13620     DMEM_AREA * larea   The area to work with.
13621   Output:
13622     none
13623   Function value:
13624     CM_SUCCESS       Successful completion
13625     DM_ACTIVE_NULL   Both area were/are full
13626     RPC_NET_ERROR    send error
13627 \********************************************************************/
13628 {
13629    INT tot_size, nwrite;
13630    char *lpt;
13631 
13632    /* if not connected remotely, use bm_send_event */
13633    if (_send_sock == 0)
13634       return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
13635 
13636    /* alias */
13637    lpt = larea->pt;
13638 
13639    /* Get overall buffer size */
13640    tot_size = (POINTER_T) larea->pe - (POINTER_T) lpt;
13641 
13642    /* shortcut for speed */
13643    if (tot_size == 0)
13644       return CM_SUCCESS;
13645 
13646 #ifdef DM_DEBUG
13647    printf("lpt:%p size:%i ", lpt, tot_size);
13648 #endif
13649    nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
13650 #ifdef DM_DEBUG
13651    printf("nwrite:%i  errno:%i\n", nwrite, errno);
13652 #endif
13653    if (nwrite < 0)
13654       return RPC_NET_ERROR;
13655 
13656    /* reset area */
13657    larea->pw = larea->pe = larea->pt;
13658    larea->full = FALSE;
13659    return CM_SUCCESS;
13660 }
13661 
13662 /********************************************************************/
13663 INT dm_area_send(void)
13664 /********************************************************************\
13665   Routine: dm_area_send
13666 
13667   Purpose: Empty the FULL area only in proper event order
13668            Meant to be use either in mfe.c scheduler on every event
13669 
13670   Dual memory scheme:
13671    DM_DUAL_THREAD : Trigger sem_send
13672    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
13673                     if dm.pa is NULL (were both full) and now both are empty
13674 
13675   Input:
13676     none
13677   Output:
13678     none
13679   Function value:
13680     CM_SUCCESS                Successful completion
13681     RPC_NET_ERROR             send error
13682 \********************************************************************/
13683 {
13684 #ifdef DM_DUAL_THREAD
13685    INT status;
13686 
13687    /* force a DM_SEND if possible. Don't wait for completion */
13688    dm.action = DM_SEND;
13689    ss_mutex_release(dm.sem_send);
13690 #ifdef OS_WINNT
13691    /* necessary for true MUTEX (NT) */
13692    status = ss_mutex_wait_for(dm.sem_send, 1);
13693    if (status == SS_NO_MUTEX) {
13694       printf(" timeout while waiting for sem_send\n");
13695       return RPC_NET_ERROR;
13696    }
13697 #endif
13698 
13699    return CM_SUCCESS;
13700 #else
13701    /* ---------- NOT IN DUAL THREAD ----------- */
13702    INT status = 0;
13703 
13704    /* if no DUAL thread everything is local then */
13705    /* select the full area */
13706    if (dm.area1.full && dm.area2.full)
13707       if (dm.area1.serial <= dm.area2.serial)
13708          status = dm_buffer_send(&dm.area1);
13709       else
13710          status = dm_buffer_send(&dm.area2);
13711    else if (dm.area1.full)
13712       status = dm_buffer_send(&dm.area1);
13713    else if (dm.area2.full)
13714       status = dm_buffer_send(&dm.area2);
13715    if (status != CM_SUCCESS)
13716       return status;            /* catch transfer error too */
13717 
13718    if (dm.pa == NULL) {
13719       printf(" sync send dm.pa:%p full 1%d 2%d\n", dm.pa, dm.area1.full, dm.area2.full);
13720       dm.pa = &dm.area1;
13721    }
13722    return CM_SUCCESS;
13723 #endif
13724 }
13725 
13726 /********************************************************************/
13727 INT dm_task(void *pointer)
13728 /********************************************************************\
13729   Routine: dm_task
13730 
13731   Purpose: async send events doing a double purpose:
13732   a) send full buffer if found (DM_SEND) set by dm_active_full
13733   b) flush full areas (DM_FLUSH) set by dm_area_flush
13734   Input:
13735   none
13736   Output:
13737   none
13738   Function value:
13739   none
13740   \********************************************************************/
13741 {
13742 #ifdef DM_DUAL_THREAD
13743    INT status, timeout;
13744 
13745    printf("Semaphores initialization ... in areaSend ");
13746    /* Check or Wait for semaphore to be setup */
13747    timeout = ss_millitime();
13748    while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
13749       ss_sleep(200);
13750    if (dm.sem_send == 0)
13751       goto kill;
13752 
13753 #ifdef OS_WINNT
13754    /* necessary for true MUTEX (NT) get semaphore */
13755    ss_mutex_wait_for(dm.sem_flush, 0);
13756 #endif
13757 
13758    /* Main FOREVER LOOP */
13759    printf("task areaSend ready...\n");
13760    while (1) {
13761       if (!dm_area_full()) {
13762          /* wait semaphore here ........ 0 == forever */
13763          ss_mutex_wait_for(dm.sem_send, 0);
13764 #ifdef OS_WINNT
13765          /* necessary for true MUTEX (NT) give semaphore */
13766          ss_mutex_release(dm.sem_send);
13767 #endif
13768       }
13769       if (dm.action == DM_SEND) {
13770 #ifdef DM_DEBUG
13771          printf("Send %i %i ", dm.area1.full, dm.area2.full);
13772 #endif
13773          /* DM_SEND : Empty the oldest buffer only. */
13774          if (dm.area1.full && dm.area2.full) {
13775             if (dm.area1.serial <= dm.area2.serial)
13776                status = dm_buffer_send(&dm.area1);
13777             else
13778                status = dm_buffer_send(&dm.area2);
13779          } else if (dm.area1.full)
13780             status = dm_buffer_send(&dm.area1);
13781          else if (dm.area2.full)
13782             status = dm_buffer_send(&dm.area2);
13783 
13784          if (status != CM_SUCCESS) {
13785             cm_msg(MERROR, "dm_task", "network error %i", status);
13786             goto kill;
13787          }
13788       } /* if DM_SEND */
13789       else if (dm.action == DM_FLUSH) {
13790          /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
13791             events) Empty both area in order independently of being full or not */
13792          if (dm.area1.serial <= dm.area2.serial) {
13793             status = dm_buffer_send(&dm.area1);
13794             if (status != CM_SUCCESS)
13795                goto error;
13796             status = dm_buffer_send(&dm.area2);
13797             if (status != CM_SUCCESS)
13798                goto error;
13799          } else {
13800             status = dm_buffer_send(&dm.area2);
13801             if (status != CM_SUCCESS)
13802                goto error;
13803             status = dm_buffer_send(&dm.area1);
13804             if (status != CM_SUCCESS)
13805                goto error;
13806          }
13807          /* reset counter */
13808          dm.area1.serial = 0;
13809          dm.area2.serial = dm.serial = 1;
13810 #ifdef DM_DEBUG
13811          printf("dm.action: Flushing ...\n");
13812 #endif
13813          /* reset area to #1 */
13814          dm.pa = &dm.area1;
13815 
13816          /* release user */
13817          ss_mutex_release(dm.sem_flush);
13818 #ifdef OS_WINNT
13819          /* necessary for true MUTEX (NT) get semaphore back */
13820          ss_mutex_wait_for(dm.sem_flush, 0);
13821 #endif
13822       }
13823       /* if FLUSH */
13824       if (dm.action == DM_KILL)
13825          goto kill;
13826 
13827    }                            /* FOREVER (go back wainting for semaphore) */
13828 
13829    /* kill spawn now */
13830  error:
13831    cm_msg(MERROR, "dm_area_flush", "aSync Net error");
13832  kill:
13833    ss_mutex_release(dm.sem_flush);
13834 #ifdef OS_WINNT
13835    ss_mutex_wait_for(dm.sem_flush, 1);
13836 #endif
13837    cm_msg(MERROR, "areaSend", "task areaSend exiting now");
13838    exit;
13839    return 1;
13840 #else
13841    printf("DM_DUAL_THREAD not defined %p\n", pointer);
13842    return 0;
13843 #endif
13844 }
13845 
13846 /********************************************************************/
13847 INT dm_area_flush(void)
13848 /********************************************************************\
13849   Routine: dm_area_flush
13850 
13851   Purpose: Flush all the events in the areas.
13852            Used in mfe for BOR events, periodic events and
13853            if rate to low in main loop once a second. The standard
13854            data transfer should be done/triggered by dm_area_send (sync/async)
13855            in dm_pointer_get().
13856   Input:
13857     none
13858   Output:
13859     none
13860   Function value:
13861     CM_SUCCESS       Successful completion
13862     RPC_NET_ERROR    send error
13863 \********************************************************************/
13864 {
13865    INT status;
13866 #ifdef DM_DUAL_THREAD
13867    /* request FULL flush */
13868    dm.action = DM_FLUSH;
13869    ss_mutex_release(dm.sem_send);
13870 #ifdef OS_WINNT
13871    /* necessary for true MUTEX (NT) get semaphore back */
13872    ss_mutex_wait_for(dm.sem_send, 0);
13873 #endif
13874 
13875    /* important to wait for completion before continue with timeout
13876       timeout specified milliseconds */
13877    status = ss_mutex_wait_for(dm.sem_flush, 10000);
13878 #ifdef DM_DEBUG
13879    printf("dm_area_flush after waiting %i\n", status);
13880 #endif
13881 #ifdef OS_WINNT
13882    ss_mutex_release(dm.sem_flush);      /* give it back now */
13883 #endif
13884 
13885    return status;
13886 #else
13887    /* full flush done here */
13888    /* select in order both area independently of being full or not */
13889    if (dm.area1.serial <= dm.area2.serial) {
13890       status = dm_buffer_send(&dm.area1);
13891       if (status != CM_SUCCESS)
13892          return status;
13893       status = dm_buffer_send(&dm.area2);
13894       if (status != CM_SUCCESS)
13895          return status;
13896    } else {
13897       status = dm_buffer_send(&dm.area2);
13898       if (status != CM_SUCCESS)
13899          return status;
13900       status = dm_buffer_send(&dm.area1);
13901       if (status != CM_SUCCESS)
13902          return status;
13903    }
13904    /* reset serial counter */
13905    dm.area1.serial = dm.area2.serial = 0;
13906    dm.last_active = ss_millitime();
13907    return CM_SUCCESS;
13908 #endif
13909 }
13910 /**dox***************************************************************/
13911 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13912 
13913 /**dox***************************************************************/
13914 /** @} *//* end of dmfunctionc */
13915 
13916 
13917 /**dox***************************************************************/
13918 /** @addtogroup rbfunctionc
13919  *
13920  *  @{  */
13921 
13922 /**dox***************************************************************/
13923 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13924 /********************************************************************/
13925 
13926 /********************************************************************\
13927 *                                                                    *
13928 *                 Ring buffer functions                              *
13929 *                                                                    *
13930 * Provide an inter-thread buffer scheme for handling front-end       *
13931 * events. This code allows concurrent data acquisition, calibration  *
13932 * and network transfer on a multi-CPU machine. One thread reads      *
13933 * out the data, passes it vis the ring buffer functions              *
13934 * to another thread running on the other CPU, which can then         *
13935 * calibrate and/or send the data over the network.                   *
13936 *                                                                    *
13937 \********************************************************************/
13938 
13939 typedef struct {
13940    unsigned char *buffer;
13941    unsigned int  size;
13942    unsigned int  max_event_size;
13943    unsigned char *rp;
13944    unsigned char *wp;
13945    unsigned char *ep;
13946 } RING_BUFFER;
13947 
13948 #define MAX_RING_BUFFER 10
13949 
13950 RING_BUFFER rb[MAX_RING_BUFFER];
13951 
13952 volatile int _rb_nonblocking = 0;
13953 
13954 /**dox***************************************************************/
13955 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13956 
13957 /********************************************************************/
13958 /**
13959 Set all rb_get_xx to nonblocking. Needed in multi-thread
13960 environments for stopping all theads without deadlock
13961 @return DB_SUCCESS
13962 */
13963 int rb_set_nonblocking()
13964 /********************************************************************\
13965 
13966   Routine: rb_set_nonblocking
13967 
13968   Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
13969            environments for stopping all theads without deadlock
13970 
13971   Input:
13972     NONE
13973 
13974   Output:
13975     NONE
13976 
13977   Function value:
13978     DB_SUCCESS       Successful completion
13979 
13980 \********************************************************************/
13981 {
13982    _rb_nonblocking = 1;
13983 
13984    return DB_SUCCESS;
13985 }
13986 
13987 /********************************************************************/
13988 /**
13989 Create a ring buffer with a given size
13990 
13991 Provide an inter-thread buffer scheme for handling front-end
13992 events. This code allows concurrent data acquisition, calibration
13993 and network transfer on a multi-CPU machine. One thread reads
13994 out the data, passes it via the ring buffer functions
13995 to another thread running on the other CPU, which can then
13996 calibrate and/or send the data over the network.
13997 
13998 @param size             Size of ring buffer, must be larger than
13999                          2*max_event_size
14000 @param max_event_size   Maximum event size to be placed into
14001 @param *handle          Handle to ring buffer
14002 @return DB_SUCCESS, DB_NO_MEMORY, DB_INVALID_PARAM
14003 */
14004 int rb_create(int size, int max_event_size, int *handle)
14005 /********************************************************************\
14006 
14007   Routine: rb_create
14008 
14009   Purpose: Create a ring buffer with a given size
14010 
14011   Input:
14012     int size             Size of ring buffer, must be larger than
14013                          2*max_event_size
14014     int max_event_size   Maximum event size to be placed into
14015                          ring buffer
14016   Output:
14017     int *handle          Handle to ring buffer
14018 
14019   Function value:
14020     DB_SUCCESS           Successful completion
14021     DB_NO_MEMORY         Maximum number of ring buffers exceeded
14022     DB_INVALID_PARAM     Invalid event size specified
14023 
14024 \********************************************************************/
14025 {
14026    int i;
14027 
14028    for (i=0 ; i<MAX_RING_BUFFER ; i++)
14029       if (rb[i].buffer == NULL)
14030          break;
14031 
14032    if (i == MAX_RING_BUFFER)
14033       return DB_NO_MEMORY;
14034 
14035    if (size < max_event_size * 2)
14036       return DB_INVALID_PARAM;
14037 
14038    memset(&rb[i], 0, sizeof(RING_BUFFER));
14039    rb[i].buffer = (unsigned char *) M_MALLOC(size);
14040    assert(rb[i].buffer);
14041    rb[i].size = size;
14042    rb[i].max_event_size = max_event_size;
14043    rb[i].rp = rb[i].buffer;
14044    rb[i].wp = rb[i].buffer;
14045    rb[i].ep = rb[i].buffer;
14046 
14047    *handle = i+1;
14048 
14049    return DB_SUCCESS;
14050 }
14051 
14052 /********************************************************************/
14053 /**
14054 Delete a ring buffer
14055 @param handle  Handle of the ring buffer
14056 @return  DB_SUCCESS
14057 */
14058 int rb_delete(int handle)
14059 /********************************************************************\
14060 
14061   Routine: rb_delete
14062 
14063   Purpose: Delete a ring buffer
14064 
14065   Input:
14066     none
14067   Output:
14068     int handle       Handle to ring buffer
14069 
14070   Function value:
14071     DB_SUCCESS       Successful completion
14072 
14073 \********************************************************************/
14074 {
14075    if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
14076       return DB_INVALID_HANDLE;
14077 
14078    M_FREE(rb[handle-1].buffer);
14079    memset(&rb[handle-1], 0, sizeof(RING_BUFFER));
14080 
14081    return DB_SUCCESS;
14082 }
14083 
14084 /********************************************************************/
14085 /**
14086 Retrieve write pointer where new data can be written
14087 @param handle               Ring buffer handle
14088 @param millisec             Optional timeout in milliseconds if
14089                               buffer is full. Zero to not wait at
14090                               all (non-blocking)
14091 @param  **p                  Write pointer
14092 @return DB_SUCCESS, DB_TIMEOUT, DB_INVALID_HANDLE
14093 */
14094 int rb_get_wp(int handle, void **p, int millisec)
14095 /********************************************************************\
14096 
14097 Routine: rb_get_wp
14098 
14099   Purpose: Retrieve write pointer where new data can be written
14100 
14101   Input:
14102      int handle               Ring buffer handle
14103      int millisec             Optional timeout in milliseconds if
14104                               buffer is full. Zero to not wait at
14105                               all (non-blocking)
14106 
14107   Output:
14108     char **p                  Write pointer
14109 
14110   Function value:
14111     DB_SUCCESS       Successful completion
14112 
14113 \********************************************************************/
14114 {
14115    int h, i;
14116    unsigned char *rp;
14117 
14118    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
14119       return DB_INVALID_HANDLE;
14120 
14121    h = handle - 1;
14122 
14123    for (i=0 ; i<=millisec/10 ; i++) {
14124 
14125       rp = rb[h].rp; // keep local copy, rb[h].rp might be changed by other thread
14126 
14127       /* check if enough size for wp >= rp without wrap-around */
14128       if (rb[h].wp >= rp &&
14129           rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
14130          *p = rb[h].wp;
14131          return DB_SUCCESS;
14132       }
14133 
14134       /* check if enough size for wp >= rp with wrap-around */
14135       if (rb[h].wp >= rp &&
14136           rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
14137           rb[h].rp > rb[h].buffer) {  // next increment of wp wraps around, so need space at beginning
14138          *p = rb[h].wp;
14139          return DB_SUCCESS;
14140       }
14141 
14142       /* check if enough size for wp < rp */
14143       if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
14144          *p = rb[h].wp;
14145          return DB_SUCCESS;
14146       }
14147 
14148       if (millisec == 0)
14149          return DB_TIMEOUT;
14150 
14151       if (_rb_nonblocking)
14152          return DB_TIMEOUT;
14153 
14154       /* wait one time slice */
14155       ss_sleep(10);
14156    }
14157 
14158    return DB_TIMEOUT;
14159 }
14160 
14161 /********************************************************************/
14162 /** rb_increment_wp
14163 
14164 Increment current write pointer, making the data at
14165 the write pointer available to the receiving thread
14166 @param handle               Ring buffer handle
14167 @param size                 Number of bytes placed at the WP
14168 @return DB_SUCCESS, DB_INVALID_PARAM, DB_INVALID_HANDLE
14169 */
14170 int rb_increment_wp(int handle, int size)
14171 /********************************************************************\
14172 
14173   Routine: rb_increment_wp
14174 
14175   Purpose: Increment current write pointer, making the data at
14176            the write pointer available to the receiving thread
14177 
14178   Input:
14179      int handle               Ring buffer handle
14180      int size                 Number of bytes placed at the WP
14181 
14182   Output:
14183     NONE
14184 
14185   Function value:
14186     DB_SUCCESS                Successful completion
14187     DB_INVALID_PARAM          Event size too large or invalid handle
14188 \********************************************************************/
14189 {
14190    int h;
14191    unsigned char *old_wp, *new_wp;
14192 
14193    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
14194       return DB_INVALID_HANDLE;
14195 
14196    h = handle - 1;
14197 
14198    if ((DWORD) size > rb[h].max_event_size)
14199       return DB_INVALID_PARAM;
14200 
14201    old_wp = rb[h].wp;
14202    new_wp = rb[h].wp + size;
14203 
14204    /* wrap around wp if not enough space */
14205    if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
14206       rb[h].ep = new_wp;
14207       new_wp = rb[h].buffer;
14208       assert(rb[h].rp != rb[h].buffer);
14209    }
14210 
14211    rb[h].wp = new_wp;
14212 
14213    return DB_SUCCESS;
14214 }
14215 
14216 /********************************************************************/
14217 /**
14218 Obtain the current read pointer at which new data is
14219 available with optional timeout
14220 
14221 @param  handle               Ring buffer handle
14222 @param  millisec             Optional timeout in milliseconds if
14223                              buffer is full. Zero to not wait at
14224                              all (non-blocking)
14225 
14226 @param **p                 Address of pointer pointing to newly
14227                              available data. If p == NULL, only
14228                              return status.
14229 @return  DB_SUCCESS, DB_TIEMOUT, DB_INVALID_HANDLE
14230 
14231 */
14232 int rb_get_rp(int handle, void **p, int millisec)
14233 /********************************************************************\
14234 
14235   Routine: rb_get_rp
14236 
14237   Purpose: Obtain the current read pointer at which new data is
14238            available with optional timeout
14239 
14240   Input:
14241     int handle               Ring buffer handle
14242     int millisec             Optional timeout in milliseconds if
14243                              buffer is full. Zero to not wait at
14244                              all (non-blocking)
14245 
14246   Output:
14247     char **p                 Address of pointer pointing to newly
14248                              available data. If p == NULL, only
14249                              return status.
14250 
14251   Function value:
14252     DB_SUCCESS       Successful completion
14253 
14254 \********************************************************************/
14255 {
14256    int i, h;
14257 
14258    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
14259       return DB_INVALID_HANDLE;
14260 
14261    h = handle - 1;
14262 
14263    for (i=0 ; i <= millisec/10 ; i++) {
14264 
14265       if (rb[h].wp != rb[h].rp) {
14266          if (p != NULL)
14267             *p = rb[handle-1].rp;
14268          return DB_SUCCESS;
14269       }
14270 
14271       if (millisec == 0)
14272          return DB_TIMEOUT;
14273 
14274       if (_rb_nonblocking)
14275          return DB_TIMEOUT;
14276 
14277       /* wait one time slice */
14278       ss_sleep(10);
14279    }
14280 
14281    return DB_TIMEOUT;
14282 }
14283 
14284 /********************************************************************/
14285 /**
14286 Increment current read pointer, freeing up space for the writing thread.
14287 
14288 @param handle               Ring buffer handle
14289 @param size                 Number of bytes to free up at current
14290                               read pointer
14291 @return  DB_SUCCESS, DB_INVALID_PARAM
14292 
14293 */
14294 int rb_increment_rp(int handle, int size)
14295 /********************************************************************\
14296 
14297   Routine: rb_increment_rp
14298 
14299   Purpose: Increment current read pointer, freeing up space for
14300            the writing thread.
14301 
14302   Input:
14303      int handle               Ring buffer handle
14304      int size                 Number of bytes to free up at current
14305                               read pointer
14306 
14307   Output:
14308     NONE
14309 
14310   Function value:
14311     DB_SUCCESS                Successful completion
14312     DB_INVALID_PARAM          Event size too large or invalid handle
14313 
14314 \********************************************************************/
14315 {
14316    int h;
14317 
14318    unsigned char *new_rp, *old_rp;
14319 
14320    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
14321       return DB_INVALID_HANDLE;
14322 
14323    h = handle - 1;
14324 
14325    if ((DWORD) size > rb[h].max_event_size)
14326       return DB_INVALID_PARAM;
14327 
14328    old_rp = rb[h].rp;
14329    new_rp = rb[h].rp + size;
14330 
14331    /* wrap around if not enough space left */
14332    if (new_rp + rb[h].max_event_size > rb[h].buffer + rb[h].size)
14333       new_rp = rb[h].buffer;
14334 
14335    rb[handle-1].rp = new_rp;
14336 
14337    return DB_SUCCESS;
14338 }
14339 
14340 /********************************************************************/
14341 /**
14342 Return number of bytes in a ring buffer
14343 
14344 @param handle              Handle of the buffer to get the info
14345 @param *n_bytes            Number of bytes in buffer
14346 @return DB_SUCCESS, DB_INVALID_HANDLE
14347 */
14348 int rb_get_buffer_level(int handle, int * n_bytes)
14349 /********************************************************************\
14350 
14351   Routine: rb_get_buffer_level
14352 
14353   Purpose: Return number of bytes in a ring buffer
14354 
14355   Input:
14356     int handle              Handle of the buffer to get the info
14357 
14358   Output:
14359     int *n_bytes            Number of bytes in buffer
14360 
14361   Function value:
14362     DB_SUCCESS              Successful completion
14363     DB_INVALID_HANDLE       Buffer handle is invalid
14364 
14365 \********************************************************************/
14366 {
14367    int h;
14368 
14369    if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle-1].buffer == NULL)
14370       return DB_INVALID_HANDLE;
14371 
14372    h = handle - 1;
14373 
14374    if (rb[h].wp >= rb[h].rp)
14375       *n_bytes = (POINTER_T)rb[h].wp - (POINTER_T)rb[h].rp;
14376    else
14377       *n_bytes = (POINTER_T)rb[h].ep - (POINTER_T)rb[h].rp + (POINTER_T)rb[h].wp - (POINTER_T)rb[h].buffer;
14378 
14379    return DB_SUCCESS;
14380 }
14381 
14382 /**dox***************************************************************/
14383 /** @} *//* end of rbfunctionc */
14384 
14385 /**dox***************************************************************/
14386 /** @} *//* end of midasincludecode */

Midas DOC Version 2.0.2 ---- 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